gnome-shell-3.6.3.1/src/shell-generic-container.c

No issues found

  1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
  2 
  3 /**
  4  * SECTION:shell-generic-container
  5  * @short_description: A container class with signals for allocation
  6  *
  7  * #ShellGenericContainer is mainly a workaround for the current
  8  * lack of GObject subclassing + vfunc overrides in gjs.  We
  9  * implement the container interface, but proxy the virtual functions
 10  * into signals, which gjs can catch.
 11  *
 12  * #ShellGenericContainer is an #StWidget, and automatically takes its
 13  * borders and padding into account during size request and allocation.
 14  */
 15 
 16 #include "config.h"
 17 
 18 #include "shell-generic-container.h"
 19 
 20 #include <clutter/clutter.h>
 21 #include <gtk/gtk.h>
 22 #include <girepository.h>
 23 
 24 static void shell_generic_container_iface_init (ClutterContainerIface *iface);
 25 
 26 G_DEFINE_TYPE_WITH_CODE(ShellGenericContainer,
 27                         shell_generic_container,
 28                         ST_TYPE_WIDGET,
 29                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
 30                                                shell_generic_container_iface_init));
 31 
 32 struct _ShellGenericContainerPrivate {
 33   GHashTable *skip_paint;
 34 };
 35 
 36 /* Signals */
 37 enum
 38 {
 39   GET_PREFERRED_WIDTH,
 40   GET_PREFERRED_HEIGHT,
 41   ALLOCATE,
 42   LAST_SIGNAL
 43 };
 44 
 45 static guint shell_generic_container_signals [LAST_SIGNAL] = { 0 };
 46 
 47 static gpointer
 48 shell_generic_container_allocation_ref (ShellGenericContainerAllocation *alloc)
 49 {
 50   alloc->_refcount++;
 51   return alloc;
 52 }
 53 
 54 static void
 55 shell_generic_container_allocation_unref (ShellGenericContainerAllocation *alloc)
 56 {
 57   if (--alloc->_refcount == 0)
 58     g_slice_free (ShellGenericContainerAllocation, alloc);
 59 }
 60 
 61 static void
 62 shell_generic_container_allocate (ClutterActor           *self,
 63                                   const ClutterActorBox  *box,
 64                                   ClutterAllocationFlags  flags)
 65 {
 66   StThemeNode *theme_node;
 67   ClutterActorBox content_box;
 68 
 69   clutter_actor_set_allocation (self, box, flags);
 70 
 71   theme_node = st_widget_get_theme_node (ST_WIDGET (self));
 72   st_theme_node_get_content_box (theme_node, box, &content_box);
 73 
 74   g_signal_emit (G_OBJECT (self), shell_generic_container_signals[ALLOCATE], 0,
 75                  &content_box, flags);
 76 }
 77 
 78 static void
 79 shell_generic_container_get_preferred_width (ClutterActor *actor,
 80                                              gfloat        for_height,
 81                                              gfloat       *min_width_p,
 82                                              gfloat       *natural_width_p)
 83 {
 84   ShellGenericContainerAllocation *alloc = g_slice_new0 (ShellGenericContainerAllocation);
 85   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
 86 
 87   st_theme_node_adjust_for_height (theme_node, &for_height);
 88 
 89   alloc->_refcount = 1;
 90   g_signal_emit (G_OBJECT (actor), shell_generic_container_signals[GET_PREFERRED_WIDTH], 0,
 91                  for_height, alloc);
 92   if (min_width_p)
 93     *min_width_p = alloc->min_size;
 94   if (natural_width_p)
 95     *natural_width_p = alloc->natural_size;
 96   shell_generic_container_allocation_unref (alloc);
 97 
 98   st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
 99 }
100 
101 static void
102 shell_generic_container_get_preferred_height (ClutterActor *actor,
103                                               gfloat        for_width,
104                                               gfloat       *min_height_p,
105                                               gfloat       *natural_height_p)
106 {
107   ShellGenericContainerAllocation *alloc = g_slice_new0 (ShellGenericContainerAllocation);
108   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
109 
110   st_theme_node_adjust_for_width (theme_node, &for_width);
111 
112   alloc->_refcount = 1;
113   g_signal_emit (G_OBJECT (actor), shell_generic_container_signals[GET_PREFERRED_HEIGHT], 0,
114                  for_width, alloc);
115   if (min_height_p)
116     *min_height_p = alloc->min_size;
117   if (natural_height_p)
118     *natural_height_p = alloc->natural_size;
119   shell_generic_container_allocation_unref (alloc);
120 
121   st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
122 }
123 
124 static void
125 shell_generic_container_paint (ClutterActor  *actor)
126 {
127   ShellGenericContainer *self = (ShellGenericContainer*) actor;
128   ClutterActor *child;
129 
130   st_widget_paint_background (ST_WIDGET (actor));
131 
132   for (child = clutter_actor_get_first_child (actor);
133        child != NULL;
134        child = clutter_actor_get_next_sibling (child))
135     {
136       if (g_hash_table_lookup (self->priv->skip_paint, child))
137         continue;
138 
139       clutter_actor_paint (child);
140     }
141 }
142 
143 static void
144 shell_generic_container_pick (ClutterActor        *actor,
145                               const ClutterColor  *color)
146 {
147   ShellGenericContainer *self = (ShellGenericContainer*) actor;
148   ClutterActor *child;
149 
150   CLUTTER_ACTOR_CLASS (shell_generic_container_parent_class)->pick (actor, color);
151 
152   for (child = clutter_actor_get_first_child (actor);
153        child != NULL;
154        child = clutter_actor_get_next_sibling (child))
155     {
156       if (g_hash_table_lookup (self->priv->skip_paint, child))
157         continue;
158 
159       clutter_actor_paint (child);
160     }
161 }
162 
163 static GList *
164 shell_generic_container_get_focus_chain (StWidget *widget)
165 {
166   ShellGenericContainer *self = SHELL_GENERIC_CONTAINER (widget);
167   ClutterActor *child;
168   GList *focus_chain;
169 
170   focus_chain = NULL;
171   for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self));
172        child != NULL;
173        child = clutter_actor_get_next_sibling (child))
174     {
175       if (CLUTTER_ACTOR_IS_VISIBLE (child) &&
176           !shell_generic_container_get_skip_paint (self, child))
177         focus_chain = g_list_prepend (focus_chain, child);
178     }
179 
180   return g_list_reverse (focus_chain);
181 }
182 
183 /**
184  * shell_generic_container_get_n_skip_paint:
185  * @self:  A #ShellGenericContainer
186  *
187  * Returns: Number of children which will not be painted.
188  */
189 guint
190 shell_generic_container_get_n_skip_paint (ShellGenericContainer  *self)
191 {
192   return g_hash_table_size (self->priv->skip_paint);
193 }
194 
195 /**
196  * shell_generic_container_get_skip_paint:
197  * @self: A #ShellGenericContainer
198  * @child: Child #ClutterActor
199  *
200  * Gets whether or not @actor is skipped when painting.
201  *
202  * Return value: %TRUE or %FALSE
203  */
204 gboolean
205 shell_generic_container_get_skip_paint (ShellGenericContainer  *self,
206                                         ClutterActor           *child)
207 {
208   return g_hash_table_lookup (self->priv->skip_paint, child) != NULL;
209 }
210 
211 /**
212  * shell_generic_container_set_skip_paint:
213  * @self: A #ShellGenericContainer
214  * @child: Child #ClutterActor
215  * @skip: %TRUE if we should skip painting
216  *
217  * Set whether or not we should skip painting @actor.  Workaround for
218  * lack of gjs ability to override _paint vfunc.
219  */
220 void
221 shell_generic_container_set_skip_paint (ShellGenericContainer  *self,
222                                         ClutterActor           *child,
223                                         gboolean                skip)
224 {
225   gboolean currently_skipping;
226 
227   currently_skipping = g_hash_table_lookup (self->priv->skip_paint, child) != NULL;
228   if (!!skip == currently_skipping)
229     return;
230 
231   if (!skip)
232     g_hash_table_remove (self->priv->skip_paint, child);
233   else
234     g_hash_table_insert (self->priv->skip_paint, child, child);
235 
236   clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
237 }
238 
239 static void
240 shell_generic_container_finalize (GObject *object)
241 {
242   ShellGenericContainer *self = (ShellGenericContainer*) object;
243 
244   g_hash_table_destroy (self->priv->skip_paint);
245 
246   G_OBJECT_CLASS (shell_generic_container_parent_class)->finalize (object);
247 }
248 
249 static void
250 shell_generic_container_class_init (ShellGenericContainerClass *klass)
251 {
252   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
253   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
254   StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
255 
256   gobject_class->finalize = shell_generic_container_finalize;
257 
258   actor_class->get_preferred_width = shell_generic_container_get_preferred_width;
259   actor_class->get_preferred_height = shell_generic_container_get_preferred_height;
260   actor_class->allocate = shell_generic_container_allocate;
261   actor_class->paint = shell_generic_container_paint;
262   actor_class->pick = shell_generic_container_pick;
263 
264   widget_class->get_focus_chain = shell_generic_container_get_focus_chain;
265 
266   /**
267    * ShellGenericContainer::get-preferred-width:
268    * @self: the #ShellGenericContainer
269    * @for_height: as in clutter_actor_get_preferred_width()
270    * @alloc: a #ShellGenericContainerAllocation to be filled in
271    *
272    * Emitted when clutter_actor_get_preferred_width() is called
273    * on @self. You should fill in the fields of @alloc with the
274    * your minimum and natural widths. #ShellGenericContainer
275    * will deal with taking its borders and padding into account
276    * for you.
277    *
278    * @alloc's fields are initialized to 0, so unless you have a fixed
279    * width specified (via #ClutterActor:width or CSS), you must
280    * connect to this signal and fill in the values.
281    */
282   shell_generic_container_signals[GET_PREFERRED_WIDTH] =
283     g_signal_new ("get-preferred-width",
284                   G_TYPE_FROM_CLASS (klass),
285                   G_SIGNAL_RUN_LAST,
286                   0,
287                   NULL, NULL, NULL,
288                   G_TYPE_NONE, 2, G_TYPE_FLOAT, SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION);
289 
290   /**
291    * ShellGenericContainer::get-preferred-height:
292    * @self: the #ShellGenericContainer
293    * @for_width: as in clutter_actor_get_preferred_height()
294    * @alloc: a #ShellGenericContainerAllocation to be filled in
295    *
296    * Emitted when clutter_actor_get_preferred_height() is called
297    * on @self. You should fill in the fields of @alloc with the
298    * your minimum and natural heights. #ShellGenericContainer
299    * will deal with taking its borders and padding into account
300    * for you.
301    *
302    * @alloc's fields are initialized to 0, so unless you have a fixed
303    * height specified (via #ClutterActor:height or CSS), you must
304    * connect to this signal and fill in the values.
305    */
306   shell_generic_container_signals[GET_PREFERRED_HEIGHT] =
307     g_signal_new ("get-preferred-height",
308                   G_TYPE_FROM_CLASS (klass),
309                   G_SIGNAL_RUN_LAST,
310                   0,
311                   NULL, NULL, NULL,
312                   G_TYPE_NONE, 2, G_TYPE_FLOAT, SHELL_TYPE_GENERIC_CONTAINER_ALLOCATION);
313 
314   /**
315    * ShellGenericContainer::allocate:
316    * @self: the #ShellGenericContainer
317    * @box: @self's content box
318    * @flags: the allocation flags.
319    *
320    * Emitted when @self is allocated, after chaining up to the parent
321    * allocate method.
322    *
323    * Note that @box is @self's content box (qv
324    * st_theme_node_get_content_box()), NOT its allocation.
325    */
326   shell_generic_container_signals[ALLOCATE] =
327     g_signal_new ("allocate",
328                   G_TYPE_FROM_CLASS (klass),
329                   G_SIGNAL_RUN_LAST,
330                   0,
331                   NULL, NULL, NULL,
332                   G_TYPE_NONE, 2, CLUTTER_TYPE_ACTOR_BOX, CLUTTER_TYPE_ALLOCATION_FLAGS);
333 
334   g_type_class_add_private (gobject_class, sizeof (ShellGenericContainerPrivate));
335 }
336 
337 static void
338 shell_generic_container_actor_removed (ClutterContainer *container,
339                                        ClutterActor     *actor)
340 {
341   ShellGenericContainerPrivate *priv = SHELL_GENERIC_CONTAINER (container)->priv;
342 
343   g_hash_table_remove (priv->skip_paint, actor);
344 }
345 
346 static void
347 shell_generic_container_iface_init (ClutterContainerIface *iface)
348 {
349   iface->actor_removed = shell_generic_container_actor_removed;
350 }
351 
352 static void
353 shell_generic_container_init (ShellGenericContainer *area)
354 {
355   area->priv = G_TYPE_INSTANCE_GET_PRIVATE (area, SHELL_TYPE_GENERIC_CONTAINER,
356                                             ShellGenericContainerPrivate);
357   area->priv->skip_paint = g_hash_table_new (NULL, NULL);
358 }
359 
360 GType
361 shell_generic_container_allocation_get_type (void)
362 {
363   static GType gtype = G_TYPE_INVALID;
364   if (gtype == G_TYPE_INVALID)
365     {
366       gtype = g_boxed_type_register_static ("ShellGenericContainerAllocation",
367          (GBoxedCopyFunc)shell_generic_container_allocation_ref,
368          (GBoxedFreeFunc)shell_generic_container_allocation_unref);
369     }
370   return gtype;
371 }