~cyphermox/ido/gtest-source

« back to all changes in this revision

Viewing changes to src/idooffscreenproxy.c

  • Committer: Charles Kerr
  • Date: 2012-03-10 01:10:25 UTC
  • mto: This revision was merged to the branch mainline in revision 102.
  • Revision ID: charles.kerr@canonical.com-20120310011025-v1zpc9liqn810q5q
remove idooffscreenproxy

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright 2011 Canonical, Ltd.
3
 
 *
4
 
 * This program is free software: you can redistribute it and/or modify it
5
 
 * under the terms of either or both of the following licenses:
6
 
 *
7
 
 * 1) the GNU Lesser General Public License version 3, as published by the
8
 
 * Free Software Foundation; and/or
9
 
 * 2) the GNU Lesser General Public License version 2.1, as published by
10
 
 * the Free Software Foundation.
11
 
 *
12
 
 * This program is distributed in the hope that it will be useful, but
13
 
 * WITHOUT ANY WARRANTY; without even the implied warranties of
14
 
 * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
15
 
 * PURPOSE.  See the applicable version of the GNU Lesser General Public
16
 
 * License for more details.
17
 
 *
18
 
 * You should have received a copy of both the GNU Lesser General Public
19
 
 * License version 3 and version 2.1 along with this program.  If not, see
20
 
 * <http://www.gnu.org/licenses/>
21
 
 *
22
 
 * Authors:
23
 
 *    Robert Carr <racarr@canonical.com>
24
 
 */
25
 
 
26
 
#include <gtk/gtk.h>
27
 
#include "idooffscreenproxy.h"
28
 
 
29
 
struct _IdoOffscreenProxyPrivate
30
 
{
31
 
  GtkWidget *child;
32
 
  
33
 
  GdkWindow *offscreen_window;
34
 
};
35
 
 
36
 
static void ido_offscreen_proxy_realize (GtkWidget *widget);
37
 
static void ido_offscreen_proxy_unrealize (GtkWidget *widget);
38
 
static void ido_offscreen_proxy_get_preferred_width (GtkWidget *widget,
39
 
                                                   gint *minimum,
40
 
                                                   gint *natural);
41
 
static void ido_offscreen_proxy_get_preferred_height (GtkWidget *widget,
42
 
                                                    gint *minimum,
43
 
                                                    gint *natural);
44
 
 
45
 
static void ido_offscreen_proxy_size_allocate (GtkWidget *widget,
46
 
                                               GtkAllocation *allocation);
47
 
static gboolean ido_offscreen_proxy_damage (GtkWidget *widget,
48
 
                                            GdkEventExpose *event);
49
 
static gboolean ido_offscreen_proxy_draw (GtkWidget *widget,
50
 
                                          cairo_t *cr);
51
 
static void ido_offscreen_proxy_add (GtkContainer *container,
52
 
                                   GtkWidget *child);
53
 
static void ido_offscreen_proxy_remove (GtkContainer *container,
54
 
                                      GtkWidget *widget);
55
 
static void ido_offscreen_proxy_forall (GtkContainer *container,
56
 
                                      gboolean include_internals,
57
 
                                      GtkCallback callback,
58
 
                                      gpointer callback_data);
59
 
static GType ido_offscreen_proxy_child_type (GtkContainer *container);
60
 
 
61
 
static cairo_surface_t * ido_offscreen_proxy_create_alpha_image_surface (GdkWindow *offscreen, gint width, gint height);
62
 
 
63
 
 
64
 
 
65
 
G_DEFINE_TYPE (IdoOffscreenProxy, ido_offscreen_proxy, GTK_TYPE_CONTAINER);
66
 
 
67
 
#define IDO_OFFSCREEN_PROXY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), IDO_TYPE_OFFSCREEN_PROXY, IdoOffscreenProxyPrivate))
68
 
 
69
 
static void
70
 
ido_offscreen_proxy_class_init (IdoOffscreenProxyClass *klass)
71
 
{
72
 
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
73
 
  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
74
 
 
75
 
  g_type_class_add_private (klass, sizeof (IdoOffscreenProxyPrivate));
76
 
  
77
 
  widget_class->realize = ido_offscreen_proxy_realize;
78
 
  widget_class->unrealize = ido_offscreen_proxy_unrealize;
79
 
  widget_class->get_preferred_width = ido_offscreen_proxy_get_preferred_width;
80
 
  widget_class->get_preferred_height = ido_offscreen_proxy_get_preferred_height;
81
 
  widget_class->size_allocate = ido_offscreen_proxy_size_allocate;
82
 
  widget_class->draw = ido_offscreen_proxy_draw;
83
 
  
84
 
  g_signal_override_class_closure (g_signal_lookup ("damage-event", GTK_TYPE_WIDGET),
85
 
                                   IDO_TYPE_OFFSCREEN_PROXY,
86
 
                                   g_cclosure_new (G_CALLBACK (ido_offscreen_proxy_damage),
87
 
                                                   NULL, NULL));
88
 
  
89
 
  container_class->add = ido_offscreen_proxy_add;
90
 
  container_class->remove = ido_offscreen_proxy_remove;
91
 
  container_class->forall = ido_offscreen_proxy_forall;
92
 
  container_class->child_type = ido_offscreen_proxy_child_type;
93
 
 
94
 
}
95
 
 
96
 
static void
97
 
ido_offscreen_proxy_init (IdoOffscreenProxy *proxy)
98
 
{
99
 
  proxy->priv = IDO_OFFSCREEN_PROXY_GET_PRIVATE (proxy);
100
 
  
101
 
  gtk_widget_set_has_window (GTK_WIDGET (proxy), TRUE);
102
 
  
103
 
  gtk_widget_set_events (GTK_WIDGET(proxy), gtk_widget_get_events (GTK_WIDGET(proxy))
104
 
    | GDK_EXPOSURE_MASK
105
 
    | GDK_POINTER_MOTION_MASK
106
 
    | GDK_BUTTON_PRESS_MASK
107
 
    | GDK_BUTTON_RELEASE_MASK
108
 
    | GDK_SCROLL_MASK
109
 
    | GDK_ENTER_NOTIFY_MASK
110
 
                         | GDK_LEAVE_NOTIFY_MASK);
111
 
  
112
 
  gtk_container_set_border_width (GTK_CONTAINER (proxy), 0);
113
 
}
114
 
 
115
 
GtkWidget *
116
 
ido_offscreen_proxy_new (void)
117
 
{
118
 
  return g_object_new (IDO_TYPE_OFFSCREEN_PROXY, NULL);
119
 
}
120
 
 
121
 
static GdkWindow *
122
 
pick_offscreen_child (GdkWindow *offscreen_window,
123
 
                       double widget_x, double widget_y,
124
 
                       IdoOffscreenProxy *proxy)
125
 
{
126
 
  GtkAllocation child_area;
127
 
  
128
 
  if (proxy->priv->child && gtk_widget_get_visible (proxy->priv->child))
129
 
    {
130
 
      gtk_widget_get_allocation (proxy->priv->child, &child_area);
131
 
      
132
 
      // if (widget_x >= 0 && widget_x < child_area.width &&
133
 
      //  widget_y >= 0 && widget_y < child_area.height)
134
 
      //return proxy->priv->offscreen_window;
135
 
      return proxy->priv->offscreen_window;
136
 
    }
137
 
  
138
 
  return NULL;
139
 
}
140
 
 
141
 
static void
142
 
offscreen_to_parent (GdkWindow *offscreen_window,
143
 
                     double offscreen_x,
144
 
                     double offscreen_y,
145
 
                     double *parent_x,
146
 
                     double *parent_y,
147
 
                     gpointer user_data)
148
 
{
149
 
  *parent_x = offscreen_x;
150
 
  *parent_y = offscreen_y;
151
 
}
152
 
 
153
 
static void
154
 
offscreen_from_parent (GdkWindow *window,
155
 
                       double parent_x,
156
 
                       double parent_y,
157
 
                       double *offscreen_x,
158
 
                       double *offscreen_y,
159
 
                       gpointer user_data)
160
 
{
161
 
  *offscreen_x = parent_x;
162
 
  *offscreen_y = parent_y;
163
 
}
164
 
 
165
 
static cairo_surface_t *
166
 
ido_offscreen_proxy_create_alpha_image_surface (GdkWindow *offscreen, gint width, gint height)
167
 
{
168
 
  return cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
169
 
}
170
 
 
171
 
static void
172
 
proxy_add_css (GtkStyleContext *sc)
173
 
{
174
 
  GtkCssProvider *provider = gtk_css_provider_new();
175
 
  const gchar *proxy_css = ".menu.ido-offscreen { \nborder-width: 0;\nborder-radius:0; \n}\n";
176
 
  
177
 
  gtk_css_provider_load_from_data (provider, proxy_css, -1, NULL);
178
 
  
179
 
  gtk_style_context_add_provider (sc, GTK_STYLE_PROVIDER (provider), 
180
 
                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
181
 
    
182
 
}
183
 
 
184
 
static void
185
 
ido_offscreen_proxy_realize (GtkWidget *widget)
186
 
{
187
 
  IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (widget);
188
 
  GtkAllocation allocation, child_area;
189
 
  GtkStyleContext *context;
190
 
  GdkWindow *window;
191
 
  GdkWindowAttr attributes;
192
 
  gint attributes_mask;
193
 
  //  GtkRequisition child_requisition;
194
 
  
195
 
  gtk_widget_set_realized (widget, TRUE);
196
 
  
197
 
  gtk_widget_get_allocation (widget, &allocation);
198
 
  
199
 
  attributes.x = allocation.x;
200
 
  attributes.y = allocation.y;
201
 
  attributes.width = allocation.width;
202
 
  attributes.height = allocation.height;
203
 
  
204
 
  attributes.window_type = GDK_WINDOW_CHILD;
205
 
  attributes.event_mask = gtk_widget_get_events (widget)
206
 
    | GDK_EXPOSURE_MASK
207
 
    | GDK_POINTER_MOTION_MASK
208
 
    | GDK_BUTTON_PRESS_MASK
209
 
    | GDK_BUTTON_RELEASE_MASK
210
 
    | GDK_SCROLL_MASK
211
 
    | GDK_ENTER_NOTIFY_MASK
212
 
    | GDK_LEAVE_NOTIFY_MASK;
213
 
  attributes.visual = gdk_screen_get_rgba_visual (gdk_screen_get_default ());//gtk_widget_get_visual (widget);
214
 
  if (!attributes.visual)
215
 
    {
216
 
      attributes.visual = gdk_screen_get_system_visual (gdk_screen_get_default ());
217
 
    }
218
 
  attributes.wclass = GDK_INPUT_OUTPUT;
219
 
  
220
 
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
221
 
  
222
 
  window = gdk_window_new (gtk_widget_get_parent_window (widget),
223
 
                           &attributes, attributes_mask);
224
 
  
225
 
  gtk_widget_set_window (widget, window);
226
 
  gdk_window_set_user_data (window, widget);
227
 
  
228
 
  g_signal_connect (window, "pick-embedded-child",
229
 
                    G_CALLBACK (pick_offscreen_child), proxy);
230
 
  
231
 
  attributes.window_type = GDK_WINDOW_OFFSCREEN;
232
 
  attributes.x = attributes.y = 0;
233
 
  
234
 
  if (proxy->priv->child && gtk_widget_get_visible (proxy->priv->child))
235
 
    {
236
 
      gtk_widget_get_allocation (proxy->priv->child, &child_area);
237
 
      attributes.width = child_area.width;
238
 
      attributes.height = child_area.height;
239
 
    }
240
 
  
241
 
  proxy->priv->offscreen_window = gdk_window_new (gtk_widget_get_root_window (widget),
242
 
                                            &attributes, attributes_mask);
243
 
  gdk_window_set_user_data (proxy->priv->offscreen_window, widget);
244
 
  
245
 
  if (proxy->priv->child)
246
 
    gtk_widget_set_parent_window (proxy->priv->child, proxy->priv->offscreen_window);
247
 
  
248
 
  gdk_offscreen_window_set_embedder (proxy->priv->offscreen_window,
249
 
                                     window);
250
 
  
251
 
  g_signal_connect(proxy->priv->offscreen_window, "create-surface",
252
 
                   G_CALLBACK (ido_offscreen_proxy_create_alpha_image_surface),
253
 
                   proxy);
254
 
  g_signal_connect (proxy->priv->offscreen_window, "to-embedder",
255
 
                    G_CALLBACK (offscreen_to_parent), NULL);
256
 
  g_signal_connect (proxy->priv->offscreen_window, "from-embedder",
257
 
                    G_CALLBACK (offscreen_from_parent), NULL);
258
 
  
259
 
  context = gtk_widget_get_style_context (widget);
260
 
  gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENUITEM);
261
 
  gtk_style_context_set_background (context, window);
262
 
  gtk_style_context_set_background (context, proxy->priv->offscreen_window);
263
 
  gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP | GTK_JUNCTION_BOTTOM | GTK_JUNCTION_LEFT | GTK_JUNCTION_RIGHT);
264
 
  
265
 
  gtk_widget_set_name (widget, "IdoOffscreenProxy");
266
 
  gtk_widget_path_iter_set_name (gtk_widget_get_path (widget), -1, "IdoOffscreenProxy");
267
 
  
268
 
  gdk_window_show (proxy->priv->offscreen_window);
269
 
}
270
 
 
271
 
static void
272
 
ido_offscreen_proxy_unrealize (GtkWidget *widget)
273
 
{
274
 
  IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (widget);
275
 
  
276
 
  gdk_window_set_user_data (proxy->priv->offscreen_window, NULL);
277
 
  gdk_window_destroy (proxy->priv->offscreen_window);
278
 
  proxy->priv->offscreen_window = NULL;
279
 
  
280
 
  GTK_WIDGET_CLASS (ido_offscreen_proxy_parent_class)->unrealize (widget);
281
 
}
282
 
 
283
 
static GType
284
 
ido_offscreen_proxy_child_type (GtkContainer *container)
285
 
{
286
 
  IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (container);
287
 
  
288
 
  if (proxy->priv->child)
289
 
    return G_TYPE_NONE;
290
 
  
291
 
  return GTK_TYPE_WIDGET;
292
 
}
293
 
 
294
 
static void
295
 
ido_offscreen_proxy_add (GtkContainer *container,
296
 
                         GtkWidget *child)
297
 
{
298
 
  IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (container);
299
 
  
300
 
  if (!proxy->priv->child)
301
 
    {
302
 
      gtk_widget_set_parent_window (child, proxy->priv->offscreen_window);
303
 
      gtk_widget_set_parent (child, GTK_WIDGET (proxy));
304
 
      proxy->priv->child = child;
305
 
    }
306
 
  else
307
 
    {
308
 
      g_warning ("IdoOffscreenProxy can only have a single child\n");
309
 
    }
310
 
}
311
 
 
312
 
static void
313
 
ido_offscreen_proxy_remove (GtkContainer *container,
314
 
                          GtkWidget *widget)
315
 
{
316
 
  IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (container);
317
 
  gboolean was_visible;
318
 
  
319
 
  was_visible = gtk_widget_get_visible (widget);
320
 
  
321
 
  if (proxy->priv->child == widget)
322
 
    {
323
 
      gtk_widget_unparent (widget);
324
 
      proxy->priv->child = NULL;
325
 
      
326
 
      if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
327
 
        gtk_widget_queue_resize (GTK_WIDGET (container));
328
 
    }
329
 
}
330
 
 
331
 
static void
332
 
ido_offscreen_proxy_forall (GtkContainer *container,
333
 
                            gboolean include_internals,
334
 
                            GtkCallback callback,
335
 
                            gpointer callback_data)
336
 
{
337
 
  IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (container);
338
 
  
339
 
  g_return_if_fail (callback != NULL);
340
 
  
341
 
  if (proxy->priv->child)
342
 
    (*callback) (proxy->priv->child, callback_data);
343
 
}
344
 
 
345
 
static void
346
 
ido_offscreen_proxy_size_request (GtkWidget *widget,
347
 
                                  GtkRequisition *requisition)
348
 
{
349
 
  IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (widget);
350
 
  int w, h;
351
 
  
352
 
  w = 0;
353
 
  h = 0;
354
 
  
355
 
  if (proxy->priv->child && gtk_widget_get_visible (proxy->priv->child))
356
 
    {
357
 
      GtkRequisition child_requisition;
358
 
      
359
 
      gtk_widget_get_preferred_size (proxy->priv->child,
360
 
                                     &child_requisition, NULL);
361
 
      w = child_requisition.width;
362
 
      h = child_requisition.height;
363
 
    }
364
 
  
365
 
  requisition->width = w;
366
 
  requisition->height = h;
367
 
 
368
 
}
369
 
 
370
 
static void
371
 
ido_offscreen_proxy_get_preferred_width (GtkWidget *widget,
372
 
                                         gint *minimum,
373
 
                                         gint *natural)
374
 
{
375
 
  GtkRequisition requisition;
376
 
  
377
 
  ido_offscreen_proxy_size_request (widget, &requisition);
378
 
  
379
 
  *minimum = *natural = requisition.width;
380
 
}
381
 
 
382
 
static void
383
 
ido_offscreen_proxy_get_preferred_height (GtkWidget *widget,
384
 
                                          gint *minimum,
385
 
                                          gint *natural)
386
 
{
387
 
  GtkRequisition requisition;
388
 
  
389
 
  ido_offscreen_proxy_size_request (widget, &requisition);
390
 
  
391
 
  *minimum = *natural = requisition.height;
392
 
 
393
 
}
394
 
 
395
 
static void
396
 
ido_offscreen_proxy_size_allocate (GtkWidget *widget,
397
 
                                   GtkAllocation *allocation)
398
 
{
399
 
  IdoOffscreenProxy *proxy;
400
 
  
401
 
  proxy = IDO_OFFSCREEN_PROXY (widget);
402
 
  
403
 
  gtk_widget_set_allocation (widget, allocation);
404
 
  
405
 
  if (gtk_widget_get_realized (widget))
406
 
    {
407
 
      gdk_window_move_resize (gtk_widget_get_window (widget),
408
 
                              allocation->x,
409
 
                              allocation->y,
410
 
                              allocation->width,
411
 
                              allocation->height);
412
 
    }
413
 
  
414
 
  if (proxy->priv->child && gtk_widget_get_visible (proxy->priv->child))
415
 
    {
416
 
      GtkRequisition child_requisition;
417
 
      GtkAllocation child_allocation;
418
 
      
419
 
      gtk_widget_get_preferred_size (proxy->priv->child,
420
 
                                     &child_requisition, NULL);
421
 
      
422
 
      child_allocation.x = child_requisition.width;
423
 
      child_allocation.y = child_requisition.height;
424
 
      child_allocation.width = allocation->width;
425
 
      child_allocation.height = allocation->height;
426
 
      
427
 
      if (gtk_widget_get_realized (widget))
428
 
        gdk_window_move_resize (proxy->priv->offscreen_window,
429
 
                                child_allocation.x,
430
 
                                child_allocation.y,
431
 
                                child_allocation.width+4,
432
 
                                child_allocation.height);
433
 
      
434
 
      child_allocation.x = child_allocation.y = 0;
435
 
      gtk_widget_size_allocate (proxy->priv->child, &child_allocation);
436
 
    }
437
 
}
438
 
 
439
 
 
440
 
static gboolean
441
 
ido_offscreen_proxy_damage (GtkWidget *widget,
442
 
                            GdkEventExpose *event)
443
 
{
444
 
  gdk_window_invalidate_rect (gtk_widget_get_window (widget),
445
 
                              NULL, FALSE);
446
 
  return TRUE;
447
 
}
448
 
 
449
 
static GtkStyleContext *
450
 
get_menu_style_context ()
451
 
{
452
 
  GtkStyleContext *sc;
453
 
  GtkWidgetPath *path;
454
 
  
455
 
  path = gtk_widget_path_new ();
456
 
  gtk_widget_path_append_type (path, GTK_TYPE_MENU);
457
 
  
458
 
  sc = gtk_style_context_new();
459
 
 
460
 
  proxy_add_css (sc);
461
 
  
462
 
  gtk_style_context_set_path (sc, path);
463
 
  gtk_style_context_add_class (sc, GTK_STYLE_CLASS_MENU);
464
 
  gtk_style_context_add_class (sc, "ido-offscreen");
465
 
  
466
 
 
467
 
  
468
 
  gtk_widget_path_free (path);
469
 
 
470
 
  return sc;
471
 
}
472
 
 
473
 
static gboolean
474
 
ido_offscreen_proxy_draw (GtkWidget *widget,
475
 
                          cairo_t *cr)
476
 
{
477
 
  IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (widget);
478
 
  GdkWindow *window;
479
 
  GtkStyleContext *sc;
480
 
  
481
 
  window = gtk_widget_get_window (widget);
482
 
  
483
 
  sc = get_menu_style_context();
484
 
 
485
 
  gtk_style_context_add_class (sc, "menubar");
486
 
 
487
 
  gtk_render_background (sc, cr,
488
 
                         0, 0,
489
 
                         gdk_window_get_width (window),
490
 
                         gdk_window_get_height (window));
491
 
  
492
 
  g_object_unref (sc);
493
 
 
494
 
  if (gtk_cairo_should_draw_window (cr, window))
495
 
    {
496
 
      cairo_surface_t *surface;
497
 
 
498
 
      if (proxy->priv->child && gtk_widget_get_visible (proxy->priv->child))
499
 
        {
500
 
          surface = gdk_offscreen_window_get_surface (proxy->priv->offscreen_window);
501
 
 
502
 
          cairo_set_source_surface (cr, surface, 0, 0);
503
 
          cairo_paint (cr);
504
 
        }
505
 
    }
506
 
    else if (gtk_cairo_should_draw_window (cr, proxy->priv->offscreen_window))
507
 
    {
508
 
      if (proxy->priv->child)
509
 
        gtk_container_propagate_draw (GTK_CONTAINER (widget),
510
 
                                      proxy->priv->child,
511
 
                                      cr);
512
 
    }
513
 
  
514
 
  return TRUE;
515
 
}