static void
mnb_zones_preview_set_property (GObject      *object,
                                guint         property_id,
                                const GValue *value,
                                GParamSpec   *pspec)
{
  MnbZonesPreviewPrivate *priv = MNB_ZONES_PREVIEW (object)->priv;

  switch (property_id)
    {
    case PROP_ZOOM:
      priv->zoom = g_value_get_double (value);
      break;

    case PROP_WORKSPACE:
      priv->workspace = g_value_get_double (value);
      break;

    case PROP_WORKSPACE_WIDTH:
      priv->width = g_value_get_uint (value);
      break;

    case PROP_WORKSPACE_HEIGHT:
      priv->height = g_value_get_uint (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      return;
    }

  clutter_actor_queue_relayout (CLUTTER_ACTOR (object));
}
static void
mnb_zones_preview_unmap (ClutterActor *actor)
{
  GList *w;
  MnbZonesPreviewPrivate *priv = MNB_ZONES_PREVIEW (actor)->priv;

  CLUTTER_ACTOR_CLASS (mnb_zones_preview_parent_class)->unmap (actor);

  for (w = priv->workspace_bins; w; w = w->next)
    clutter_actor_unmap (CLUTTER_ACTOR (w->data));
}
static void
mnb_zones_preview_get_preferred_height (ClutterActor *actor,
                                        gfloat        for_width,
                                        gfloat       *min_height_p,
                                        gfloat       *nat_height_p)
{
  MnbZonesPreviewPrivate *priv = MNB_ZONES_PREVIEW (actor)->priv;

  if (min_height_p)
    *min_height_p = priv->height;
  if (nat_height_p)
    *nat_height_p = priv->height;
}
static void
mnb_zones_preview_paint (ClutterActor *actor)
{
  GList *w;
  MnbZonesPreviewPrivate *priv = MNB_ZONES_PREVIEW (actor)->priv;

  /* Chain up for background */
  CLUTTER_ACTOR_CLASS (mnb_zones_preview_parent_class)->paint (actor);

  /* Paint bins */
  for (w = priv->workspace_bins; w; w = w->next)
    clutter_actor_paint (CLUTTER_ACTOR (w->data));
}
static void
mnb_zones_preview_allocate (ClutterActor           *actor,
                            const ClutterActorBox  *box,
                            ClutterAllocationFlags  flags)
{
  gint n;
  GList *w;
  gfloat origin, bin_width;

  MnbZonesPreviewPrivate *priv = MNB_ZONES_PREVIEW (actor)->priv;

  CLUTTER_ACTOR_CLASS (mnb_zones_preview_parent_class)->
    allocate (actor, box, flags);

  /* Figure out the origin */
  bin_width = priv->width + priv->spacing;
  origin = - (priv->workspace * bin_width * priv->zoom);

  /* Make sure we zoom out from the centre */
  origin += (bin_width - (bin_width * priv->zoom)) / 2.0;

  for (n = 0, w = priv->workspace_bins; w; w = w->next, n++)
    {
      ClutterActorBox child_box;
      gfloat width, height;
      MxPadding padding;

      ClutterActor *bin = CLUTTER_ACTOR (w->data);

      clutter_actor_get_preferred_size (bin, NULL, NULL, &width, &height);
      width *= priv->zoom;
      height *= priv->zoom;

      mx_widget_get_padding (MX_WIDGET (bin), &padding);

      /* We allocate the preferred size, but we make sure the padding is
       * 'outside' the target area.
       */
      child_box.x1 = origin - padding.left;
      child_box.x2 = child_box.x1 + width;
      child_box.y1 = (((box->y2 - box->y1) -
                       (height - padding.top - padding.bottom)) / 2.f) -
                     padding.top;
      child_box.y2 = child_box.y1 + height;

      clutter_actor_allocate (bin, &child_box, flags);

      origin = (child_box.x2 - padding.right) + (priv->spacing * priv->zoom);
    }
}
static void
mnb_zones_preview_dispose (GObject *object)
{
  MnbZonesPreview *self = MNB_ZONES_PREVIEW (object);
  MnbZonesPreviewPrivate *priv = self->priv;

  mnb_zones_preview_clear (self);

  if (priv->workspace_bg)
    {
      g_object_unref (priv->workspace_bg);
      priv->workspace_bg = NULL;
    }

  G_OBJECT_CLASS (mnb_zones_preview_parent_class)->dispose (object);
}
/*
 * This is the Metacity entry point for the effect.
 */
void
mnb_switch_zones_effect (MetaPlugin         *plugin,
                         gint                from,
                         gint                to,
                         MetaMotionDirection direction)
{
  GList *w;
  gint width, height;
  MetaScreen *screen;
  ClutterActor *window_group;

  if (running++)
    {
      /*
       * We have been called while the effect is already in progress; we need to
       * mutter know that we completed the previous run.
       */
      if (--running < 0)
        {
          g_warning (G_STRLOC ": error in running effect accounting!");
          running = 0;
        }

      meta_plugin_switch_workspace_completed (plugin);
    }

  if ((from == to) && !zones_preview)
    {
      if (--running < 0)
        {
          g_warning (G_STRLOC ": error in running effect accounting!");
          running = 0;
        }

      meta_plugin_switch_workspace_completed (plugin);

      return;
    }

  screen = meta_plugin_get_screen (plugin);

  if (!zones_preview)
    {
      ClutterActor *stage;

      /* Construct the zones preview actor */
      zones_preview = mnb_zones_preview_new ();
      g_object_set (G_OBJECT (zones_preview),
                    "workspace", (gdouble)from,
                    NULL);

      /* Add it to the stage */
      stage = meta_get_stage_for_screen (screen);
      clutter_container_add_actor (CLUTTER_CONTAINER (stage), zones_preview);

      /* Attach to completed signal */
      g_signal_connect (zones_preview, "switch-completed",
                        G_CALLBACK (mnb_switch_zones_completed_cb), plugin);
    }

  meta_screen_get_size (screen, &width, &height);
  g_object_set (G_OBJECT (zones_preview),
                "workspace-width", (guint)width,
                "workspace-height", (guint)height,
                NULL);

  mnb_zones_preview_clear (MNB_ZONES_PREVIEW (zones_preview));
  mnb_zones_preview_set_n_workspaces (MNB_ZONES_PREVIEW (zones_preview),
                                      meta_screen_get_n_workspaces (screen));

  /* Add windows to zone preview actor */
  for (w = meta_get_window_actors (screen); w; w = w->next)
    {
      MetaWindowActor *window_actor = w->data;
      gint workspace = meta_window_actor_get_workspace (window_actor);
      MetaWindow *window = meta_window_actor_get_meta_window (window_actor);
      MetaWindowType type = meta_window_get_window_type (window);

      /*
       * Only show regular windows that are not sticky (getting stacking order
       * right for sticky windows would be really hard, and since they appear
       * on each workspace, they do not help in identifying which workspace
       * it is).
       */
      if ((workspace < 0) ||
          meta_window_actor_is_override_redirect (window_actor) ||
          (type != META_WINDOW_NORMAL))
        continue;

      mnb_zones_preview_add_window (MNB_ZONES_PREVIEW (zones_preview), window_actor);
    }

  /* Make sure it's on top */
  window_group = meta_get_window_group_for_screen (screen);
  clutter_actor_raise (zones_preview, window_group);

  /* Initiate animation */
  mnb_zones_preview_change_workspace (MNB_ZONES_PREVIEW (zones_preview), to);
}