Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [platform-swt-dev] GTK in CVS

Boris Shingarov <Boris_Shingarov@xxxxxxx> writes: 
> This is a separate problem which caused us lots of grief implementing 
> Control.moveAbove()/moveBelow().  It's currently broken, and I just don't 
> know what to do about it.

Custom widget. I've appended such a widget, EclipseFixed. It probably
has a bug or three, but should give you the idea. Rewriting it in Java
sounds really painful to me but you could try it. ;-) Remove the
"main" function at the end when using in a library.

I also made it a NO_WINDOW widget, I think you are going to want to
kill off as many window widgets as possible given the synchronous
sizing/stacking handling, or there'll be flicker problems. I'm hoping
we can keep redrawing in the idle handler instead of synchronous, or
there will be flicker problems from that too.

In GTK 2.0 buttons, scrollbars, and a couple other things are changed
to NO_WINDOW widgets which will help. Basically anything that could
easily be a NO_WINDOW widget has become one because you can't move a
set of X windows atomically, which means you inherently get
flicker/tearing if you have subwindows inside the toplevel.

To use NO_WINDOW throughout, some custom event routing may be required.
On the GTK level NO_WINDOW widgets can't get events for historical
reasons, but in SWT you can dispatch events to the widget they are
"inside" instead of on the widget that GTK says they are on - i.e. if
you get an event on a GtkContainer, check if the event is inside the
bounds of a child widget, and if so dispatch it to the child rather
than the container. If that makes no sense I can try to show you what
the code would look like. GTK would just do this natively if it
weren't for backward compat concerns.
  
This is way better than e.g. putting an event box around every label
which is what SWT Label seems to do now - this is inefficient,
flickery, etc. 

> The relevant spots are UtilFuncs.setLocation()/setSize(),
> Control.setBounds() and the guys it calls, and
> Control.computeSize().  The basic problem is that widgets in SWT
> want to have full control of positioning, and use no help from GTK's
> layout containers like the GtkBoxes.  The GtkFixed doesn't quite
> leave the children alone, and set_uposition / set_usize don't fully
> work - uposition because you can't have a negative orogin, and usize
> because it screws up the child's notion of requisition.  We solve
> this by using the set_usize on a bin whose only function is to hold
> the child - the child expands to occupy the whole bin, so always
> stays the right size, and is still able to answer the requisition
> when we want to computeSize().

I think the custom widget solves this well. Just use
eclipse_fixed_set_size()/eclipse_fixed_set_location() and don't set
the usize or uposition.

As another way to address flicker, you might try implementing SWT
getBounds() by getting the info from the EclipseFixed custom widget,
and _don't_ do the GTK+ gtk_widget_size_allocate() synchronously.

Basically my opinion is that the right thing to do is to hack the
custom widget and/or the Java part of GTK SWT to store the synchronous
information you need, and let GTK+ do its actual
moving/resizing/redrawing in the idle handlers. This will save so much
pain if it can be done. 

It should not fundamentally make that much difference, because X is
async anyway, so even if you do things like force GTK to size
allocate, the X window is still not actually moved sychronously. So it
should be possible to get things to work correctly while leaving GTK
async.

If you do the event dispatching to NO_WINDOW widgets though, you
likely want to do the dispatching based on widget->allocation instead
of the rectangle stored in the EclipseFixed, because
widget->allocation will reflect GTK's actual state better.


If you want to get philosophical about the change I'm suggesting:
essentially what you are doing now is overloading widget->allocation
and gtk_widget_size_request()/gtk_widget_size_allocate() to mean
something closer to the SWT concepts. So this involves all kinds of
hacks to unbreak GTK, because GTK wants to put its concepts in there.
You can't have one data structure that means two things.

So what I think you should do instead is keep your concepts in your
own data structure - such as EclipseFixed's EclipseFixedChild struct -
and leave GTK's data structures with their original semantics. At that
point you can leave GTK alone to do its thing, and GTK will leave you
alone to do your thing. This will be substantially less fragile.

Anyway, that's my opinion at the moment. I'm obviously not an SWT expert.

Havoc

/* Copyright 2001, Havoc Pennington */

/* Derived from GtkFixed from GTK+:
 * GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

#ifndef __ECLIPSE_FIXED_H__
#define __ECLIPSE_FIXED_H__


#include <gdk/gdk.h>
#include <gtk/gtkcontainer.h>


#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */


#define ECLIPSE_TYPE_FIXED                  (eclipse_fixed_get_type ())
#define ECLIPSE_FIXED(obj)                  (GTK_CHECK_CAST ((obj), ECLIPSE_TYPE_FIXED, EclipseFixed))
#define ECLIPSE_FIXED_CLASS(klass)          (GTK_CHECK_CLASS_CAST ((klass), ECLIPSE_TYPE_FIXED, EclipseFixedClass))
#define GTK_IS_FIXED(obj)               (GTK_CHECK_TYPE ((obj), ECLIPSE_TYPE_FIXED))
#define GTK_IS_FIXED_CLASS(klass)       (GTK_CHECK_CLASS_TYPE ((klass), ECLIPSE_TYPE_FIXED))


typedef struct _EclipseFixed        EclipseFixed;
typedef struct _EclipseFixedClass   EclipseFixedClass;
typedef struct _EclipseFixedChild   EclipseFixedChild;

struct _EclipseFixed
{
  GtkContainer container;

  GList *children;
};

struct _EclipseFixedClass
{
  GtkContainerClass parent_class;
};

struct _EclipseFixedChild
{
  GtkWidget *widget;
  int x;
  int y;
  int width;
  int height;
};

GtkType    eclipse_fixed_get_type (void);
GtkWidget* eclipse_fixed_new      (void);

void eclipse_fixed_set_location (EclipseFixed *fixed,
                                 GtkWidget    *widget,
                                 int           x,
                                 int           y);
void eclipse_fixed_set_size     (EclipseFixed *fixed,
                                 GtkWidget    *widget,
                                 int           width,
                                 int           height);
void eclipse_fixed_move_above   (EclipseFixed *fixed,
                                 GtkWidget    *widget,
                                 GtkWidget    *sibling);
void eclipse_fixed_move_below   (EclipseFixed *fixed,
                                 GtkWidget    *widget,
                                 GtkWidget    *sibling);




#ifdef __cplusplus
}
#endif /* __cplusplus */


#endif /* __ECLIPSE_FIXED_H__ */
/* Copyright 2001, Havoc Pennington */

/* Derived from EclipseFixed in GTK+:
 * GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/*
 * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

#include "eclipsefixed.h"


static void eclipse_fixed_class_init    (EclipseFixedClass    *klass);
static void eclipse_fixed_init          (EclipseFixed         *fixed);
static void eclipse_fixed_map           (GtkWidget        *widget);
static void eclipse_fixed_realize       (GtkWidget        *widget);
static void eclipse_fixed_size_request  (GtkWidget        *widget,
                                         GtkRequisition   *requisition);
static void eclipse_fixed_size_allocate (GtkWidget        *widget,
                                         GtkAllocation    *allocation);
static void eclipse_fixed_paint         (GtkWidget        *widget,
                                         GdkRectangle     *area);
static void eclipse_fixed_draw          (GtkWidget        *widget,
                                         GdkRectangle     *area);
static gint eclipse_fixed_expose        (GtkWidget        *widget,
                                         GdkEventExpose   *event);
static void eclipse_fixed_add           (GtkContainer     *container,
                                         GtkWidget        *widget);
static void eclipse_fixed_remove        (GtkContainer     *container,
                                         GtkWidget        *widget);
static void eclipse_fixed_forall        (GtkContainer     *container,
                                         gboolean 	       include_internals,
                                         GtkCallback       callback,
                                         gpointer          callback_data);
static GtkType eclipse_fixed_child_type (GtkContainer     *container);

static void move_gdk_window_above (GdkWindow *window,
                                   GdkWindow *sibling);

static void move_gdk_window_below (GdkWindow *window,
                                   GdkWindow *sibling);

static GtkContainerClass *parent_class = NULL;


GtkType
eclipse_fixed_get_type (void)
{
  static GtkType fixed_type = 0;

  if (!fixed_type)
    {
      static const GtkTypeInfo fixed_info =
      {
	"EclipseFixed",
	sizeof (EclipseFixed),
	sizeof (EclipseFixedClass),
	(GtkClassInitFunc) eclipse_fixed_class_init,
	(GtkObjectInitFunc) eclipse_fixed_init,
	/* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
        (GtkClassInitFunc) NULL,
      };

      fixed_type = gtk_type_unique (GTK_TYPE_CONTAINER, &fixed_info);
    }

  return fixed_type;
}

static void
eclipse_fixed_class_init (EclipseFixedClass *class)
{
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

  object_class = (GtkObjectClass*) class;
  widget_class = (GtkWidgetClass*) class;
  container_class = (GtkContainerClass*) class;

  parent_class = gtk_type_class (GTK_TYPE_CONTAINER);

  widget_class->map = eclipse_fixed_map;
  widget_class->realize = eclipse_fixed_realize;
  widget_class->size_request = eclipse_fixed_size_request;
  widget_class->size_allocate = eclipse_fixed_size_allocate;
  widget_class->draw = eclipse_fixed_draw;
  widget_class->expose_event = eclipse_fixed_expose;

  container_class->add = eclipse_fixed_add;
  container_class->remove = eclipse_fixed_remove;
  container_class->forall = eclipse_fixed_forall;
  container_class->child_type = eclipse_fixed_child_type;
}

static GtkType
eclipse_fixed_child_type (GtkContainer     *container)
{
  return GTK_TYPE_WIDGET;
}

static void
eclipse_fixed_init (EclipseFixed *fixed)
{
  GTK_WIDGET_SET_FLAGS (fixed, GTK_NO_WINDOW);
 
  fixed->children = NULL;
}

GtkWidget*
eclipse_fixed_new (void)
{
  EclipseFixed *fixed;

  fixed = gtk_type_new (ECLIPSE_TYPE_FIXED);
  return GTK_WIDGET (fixed);
}

static EclipseFixedChild*
get_child_info (EclipseFixed *fixed,
                GtkWidget    *widget)
{
  GList *children;
  EclipseFixedChild *child_info;

  children = fixed->children;
  while (children)
    {
      child_info = children->data;
      children = children->next;

      if (child_info->widget == widget)
        return child_info;
    }

  return NULL;
}

void
eclipse_fixed_set_location (EclipseFixed *fixed,
                            GtkWidget *widget,
                            int x,
                            int y)
{
  EclipseFixedChild *child_info;

  g_return_if_fail (fixed != NULL);
  g_return_if_fail (GTK_IS_FIXED (fixed));
  g_return_if_fail (widget != NULL);
  g_return_if_fail (widget->parent == GTK_WIDGET (fixed));
  
  child_info = get_child_info (fixed, widget);

  if (child_info->x == x && child_info->y == y)
    return; /* nothing to do */
  
  child_info->x = x;
  child_info->y = y;

  if (GTK_WIDGET_VISIBLE (fixed) && GTK_WIDGET_VISIBLE (widget))
    gtk_widget_queue_resize (GTK_WIDGET (fixed));
}

/* set size to -1 to use natural size */
void
eclipse_fixed_set_size (EclipseFixed *fixed,
                        GtkWidget *widget,
                        int        width,
                        int        height)
{
  EclipseFixedChild *child_info;

  g_return_if_fail (fixed != NULL);
  g_return_if_fail (GTK_IS_FIXED (fixed));
  g_return_if_fail (widget != NULL);
  g_return_if_fail (widget->parent == GTK_WIDGET (fixed));
  
  child_info = get_child_info (fixed, widget);

  if (child_info->width == width && child_info->height == height)
    return; /* nothing to do */
  
  child_info->width = width;
  child_info->height = height;

  if (GTK_WIDGET_VISIBLE (fixed) && GTK_WIDGET_VISIBLE (widget))
    gtk_widget_queue_resize (GTK_WIDGET (fixed));
}


static GList*
find_link (EclipseFixed *fixed,
           GtkWidget    *widget)
{
  GList *tmp;

  tmp = fixed->children;

  while (tmp != NULL)
    {
      if (((EclipseFixedChild*)tmp->data)->widget == widget)
        return tmp;

      tmp = tmp->next;
    }

  return NULL;
}

static void
find_sibling_windows (EclipseFixed *fixed,
                      GtkWidget    *widget,
                      GdkWindow   **above,
                      GdkWindow   **below)
{
  GList *tmp;
  gboolean seen_ourselves;

  seen_ourselves = FALSE;

  if (below)
    *below = NULL;
  if (above)
    *above = NULL;

  tmp = fixed->children;
  while (tmp != NULL)
    {
      EclipseFixedChild *child = tmp->data;

      if (child->widget == widget)
        {
          seen_ourselves = TRUE;
        }
      else if (below && !seen_ourselves)
        {
          if (*below == NULL &&
              !GTK_WIDGET_NO_WINDOW (child->widget) &&
              GTK_WIDGET_REALIZED (child->widget))
            *below = child->widget->window;
        }
      else if (above && seen_ourselves)
        {
          if (*above == NULL &&
              !GTK_WIDGET_NO_WINDOW (child->widget) &&
              GTK_WIDGET_REALIZED (child->widget))
            *above = child->widget->window;
        }
      
      tmp = tmp->next;
    }
}

/* sibling == NULL means raise to top */
void
eclipse_fixed_move_above (EclipseFixed *fixed,
                          GtkWidget    *widget,
                          GtkWidget    *sibling)
{
  GList *sibling_link;
  GList *widget_link;
  
  g_return_if_fail (fixed != NULL);
  g_return_if_fail (GTK_IS_FIXED (fixed));
  g_return_if_fail (widget != sibling);
  g_return_if_fail (widget->parent == GTK_WIDGET (fixed));
  g_return_if_fail (sibling == NULL || sibling->parent == GTK_WIDGET (fixed));
  
  sibling_link = find_link (fixed, sibling);  
  widget_link = find_link (fixed, widget);

  g_assert (widget_link);
  
  /* remove widget link */
  fixed->children = g_list_remove_link (fixed->children, widget_link);
  
  if (sibling_link)
    {
      GdkWindow *above, *below;
      
      widget_link->prev = sibling_link;
      widget_link->next = sibling_link->next;
      if (widget_link->next)
        widget_link->next->prev = widget_link;
      sibling_link->next = widget_link;

      if (!GTK_WIDGET_NO_WINDOW (widget) && GTK_WIDGET_REALIZED (widget))
        {
          find_sibling_windows (fixed, widget, &above, &below);
          if (below)
            move_gdk_window_above (widget->window, below);
          else if (above)
            move_gdk_window_below (widget->window, above);
        }
    }
  else
    {
      if (!GTK_WIDGET_NO_WINDOW (widget) && GTK_WIDGET_REALIZED (widget))
        gdk_window_raise (widget->window);
      
      fixed->children = g_list_append (fixed->children, widget_link->data);
      g_list_free_1 (widget_link);
    }

  /* Redraw no-window widgets, window widgets will do fine
   * on their own
   */
  if (GTK_WIDGET_NO_WINDOW (widget))
    gtk_widget_queue_draw (widget);
  if (sibling && GTK_WIDGET_NO_WINDOW (sibling))
    gtk_widget_queue_draw (sibling);
}

/* sibling == NULL means lower to bottom */
void
eclipse_fixed_move_below (EclipseFixed *fixed,
                          GtkWidget    *widget,
                          GtkWidget    *sibling)
{
  GList *sibling_link;
  GList *widget_link;
  
  g_return_if_fail (fixed != NULL);
  g_return_if_fail (GTK_IS_FIXED (fixed));
  g_return_if_fail (widget != sibling);
  g_return_if_fail (widget->parent == GTK_WIDGET (fixed));
  g_return_if_fail (sibling == NULL || sibling->parent == GTK_WIDGET (fixed));
  
  sibling_link = find_link (fixed, sibling);  
  widget_link = find_link (fixed, widget);

  g_assert (widget_link);
  
  /* remove widget link */
  fixed->children = g_list_remove_link (fixed->children, widget_link);
  
  if (sibling_link)
    {
      GdkWindow *above, *below;
      
      widget_link->next = sibling_link;
      widget_link->prev = sibling_link->prev;
      if (widget_link->prev)
        widget_link->prev->next = widget_link;
      sibling_link->prev = widget_link;
      if (widget_link->prev == NULL)
        fixed->children = widget_link;

      if (!GTK_WIDGET_NO_WINDOW (widget) && GTK_WIDGET_REALIZED (widget))
        {
          find_sibling_windows (fixed, widget, &above, &below);
          if (above)
            move_gdk_window_below (widget->window, above);
          else if (below)
            move_gdk_window_above (widget->window, below);
        }
    }
  else
    {
      if (!GTK_WIDGET_NO_WINDOW (widget) && GTK_WIDGET_REALIZED (widget))
        gdk_window_lower (widget->window);      
      
      fixed->children = g_list_prepend (fixed->children, widget_link->data);
      g_list_free_1 (widget_link);
    }

  /* Redraw no-window widgets, window widgets will do fine
   * on their own
   */
  if (GTK_WIDGET_NO_WINDOW (widget))
    gtk_widget_queue_draw (widget);
  if (sibling && GTK_WIDGET_NO_WINDOW (sibling))
    gtk_widget_queue_draw (sibling);
}

static void
eclipse_fixed_map (GtkWidget *widget)
{
  EclipseFixed *fixed;
  EclipseFixedChild *child;
  GList *children;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_FIXED (widget));

  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
  fixed = ECLIPSE_FIXED (widget);

  /* First we need to realize all children and fix their
   * stacking order, before we map them
   */
  children = fixed->children;
  while (children)
    {
      child = children->data;
      children = children->next;

      if (GTK_WIDGET_VISIBLE (child->widget) &&
	  !GTK_WIDGET_REALIZED (child->widget))
	gtk_widget_realize (child->widget);

      if (!GTK_WIDGET_NO_WINDOW (child->widget))
        gdk_window_raise (child->widget->window);
    }
  
  children = fixed->children;
  while (children)
    {
      child = children->data;
      children = children->next;

      if (GTK_WIDGET_VISIBLE (child->widget) &&
	  !GTK_WIDGET_MAPPED (child->widget))
	gtk_widget_map (child->widget);
    }
}

static void
eclipse_fixed_realize (GtkWidget *widget)
{
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_FIXED (widget));

  GTK_WIDGET_CLASS (parent_class)->realize (widget);  
}

static void
eclipse_fixed_size_request (GtkWidget      *widget,
                            GtkRequisition *requisition)
{
  EclipseFixed *fixed;  
  EclipseFixedChild *child;
  GList *children;
  GtkRequisition child_requisition;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_FIXED (widget));
  g_return_if_fail (requisition != NULL);

  fixed = ECLIPSE_FIXED (widget);
  requisition->width = 0;
  requisition->height = 0;

  children = fixed->children;
  while (children)
    {
      child = children->data;
      children = children->next;

      if (GTK_WIDGET_VISIBLE (child->widget))
	{
          int w, h;
          
          gtk_widget_size_request (child->widget, &child_requisition);

          w = child->width < 0 ? child_requisition.width : child->width;
          h = child->height < 0 ? child_requisition.height : child->height;
          
          requisition->height = MAX (requisition->height,
                                     child->y + h);
          requisition->width = MAX (requisition->width,
                                    child->x + w);
	}
    }

  requisition->height += GTK_CONTAINER (fixed)->border_width * 2;
  requisition->width += GTK_CONTAINER (fixed)->border_width * 2;
}

static void
eclipse_fixed_size_allocate (GtkWidget     *widget,
                             GtkAllocation *allocation)
{
  EclipseFixed *fixed;
  EclipseFixedChild *child;
  GtkAllocation child_allocation;
  GtkRequisition child_requisition;
  GList *children;
  int border_width;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_FIXED(widget));
  g_return_if_fail (allocation != NULL);

  fixed = ECLIPSE_FIXED (widget);

  widget->allocation = *allocation;

  border_width = GTK_CONTAINER (fixed)->border_width;
  
  children = fixed->children;
  while (children)
    {
      child = children->data;
      children = children->next;
      
      if (GTK_WIDGET_VISIBLE (child->widget))
	{
	  gtk_widget_get_child_requisition (child->widget, &child_requisition);

	  child_allocation.x = child->x + border_width;
	  child_allocation.y = child->y + border_width;
	  child_allocation.width = child->width < 0 ? child_requisition.width : child->width;
	  child_allocation.height = child->height < 0 ? child_requisition.height : child->height;
	  gtk_widget_size_allocate (child->widget, &child_allocation);
	}
    }
}

static void
eclipse_fixed_paint (GtkWidget    *widget,
                     GdkRectangle *area)
{
  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_FIXED (widget));
  g_return_if_fail (area != NULL);

  if (GTK_WIDGET_DRAWABLE (widget))
    gdk_window_clear_area (widget->window,
			   area->x, area->y,
			   area->width, area->height);
}

static void
eclipse_fixed_draw (GtkWidget    *widget,
                    GdkRectangle *area)
{
  EclipseFixed *fixed;
  EclipseFixedChild *child;
  GdkRectangle child_area;
  GList *children;

  g_return_if_fail (widget != NULL);
  g_return_if_fail (GTK_IS_FIXED (widget));

  /* Note that we draw from front of fixed->children to back
   * which means the stacking order is supposed to be that
   */
  if (GTK_WIDGET_DRAWABLE (widget))
    {
      fixed = ECLIPSE_FIXED (widget);
      eclipse_fixed_paint (widget, area);

      children = fixed->children;
      while (children)
	{
	  child = children->data;
	  children = children->next;

	  if (gtk_widget_intersect (child->widget, area, &child_area))
	    gtk_widget_draw (child->widget, &child_area);
	}
    }
}

static gint
eclipse_fixed_expose (GtkWidget      *widget,
                      GdkEventExpose *event)
{
  EclipseFixed *fixed;
  EclipseFixedChild *child;
  GdkEventExpose child_event;
  GList *children;

  g_return_val_if_fail (widget != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_FIXED (widget), FALSE);
  g_return_val_if_fail (event != NULL, FALSE);

  if (GTK_WIDGET_DRAWABLE (widget))
    {
      fixed = ECLIPSE_FIXED (widget);

      child_event = *event;

      children = fixed->children;
      while (children)
	{
	  child = children->data;
	  children = children->next;

	  if (GTK_WIDGET_NO_WINDOW (child->widget) &&
	      gtk_widget_intersect (child->widget, &event->area, 
				    &child_event.area))
	    gtk_widget_event (child->widget, (GdkEvent*) &child_event);
	}
    }

  return FALSE;
}

static void
eclipse_fixed_add (GtkContainer *container,
                   GtkWidget    *widget)
{
  EclipseFixedChild *child_info;
  EclipseFixed *fixed;
  
  g_return_if_fail (container != NULL);
  g_return_if_fail (GTK_IS_FIXED (container));
  g_return_if_fail (widget != NULL);

  fixed = ECLIPSE_FIXED (container);
  
  child_info = g_new (EclipseFixedChild, 1);
  child_info->widget = widget;
  child_info->x = 0;
  child_info->y = 0;
  child_info->width = -1;
  child_info->height = -1;
  
  gtk_widget_set_parent (widget, GTK_WIDGET (fixed));

  /* Add at top of stacking order (append) */
  fixed->children = g_list_append (fixed->children, child_info);
  
  if (GTK_WIDGET_REALIZED (fixed))
    gtk_widget_realize (widget);

  if (GTK_WIDGET_VISIBLE (fixed) && GTK_WIDGET_VISIBLE (widget))
    {
      if (GTK_WIDGET_MAPPED (fixed))
	gtk_widget_map (widget);
      
      gtk_widget_queue_resize (GTK_WIDGET (fixed));
    }
}

static void
eclipse_fixed_remove (GtkContainer *container,
                      GtkWidget    *widget)
{
  EclipseFixed *fixed;
  EclipseFixedChild *child;
  GList *children;

  g_return_if_fail (container != NULL);
  g_return_if_fail (GTK_IS_FIXED (container));
  g_return_if_fail (widget != NULL);

  fixed = ECLIPSE_FIXED (container);

  children = fixed->children;
  while (children)
    {
      child = children->data;

      if (child->widget == widget)
	{
	  gboolean was_visible = GTK_WIDGET_VISIBLE (widget);
	  
	  gtk_widget_unparent (widget);

	  fixed->children = g_list_remove_link (fixed->children, children);
	  g_list_free (children);
	  g_free (child);

	  if (was_visible && GTK_WIDGET_VISIBLE (container))
	    gtk_widget_queue_resize (GTK_WIDGET (container));

	  break;
	}

      children = children->next;
    }
}

static void
eclipse_fixed_forall (GtkContainer *container,
                      gboolean	include_internals,
                      GtkCallback   callback,
                      gpointer      callback_data)
{
  EclipseFixed *fixed;
  EclipseFixedChild *child;
  GList *children;

  g_return_if_fail (container != NULL);
  g_return_if_fail (GTK_IS_FIXED (container));
  g_return_if_fail (callback != NULL);

  fixed = ECLIPSE_FIXED (container);

  children = fixed->children;
  while (children)
    {
      child = children->data;
      children = children->next;

      (* callback) (child->widget, callback_data);
    }
}

#include <gdk/gdkx.h>

static void
move_gdk_window_above (GdkWindow *window,
                       GdkWindow *sibling)
{

  XWindowChanges changes;

  changes.sibling = GDK_WINDOW_XWINDOW (sibling);
  changes.stack_mode = Above;
                  
  XConfigureWindow (gdk_display,
                    GDK_WINDOW_XWINDOW (window),
                    CWSibling | CWStackMode,
                    &changes);
}

static void
move_gdk_window_below (GdkWindow *window,
                       GdkWindow *sibling)
{

  XWindowChanges changes;

  changes.sibling = GDK_WINDOW_XWINDOW (sibling);
  changes.stack_mode = Below;

  XConfigureWindow (gdk_display,
                    GDK_WINDOW_XWINDOW (window),
                    CWSibling | CWStackMode,
                    &changes);
}


#if 1
/* Test program */
#include <gtk/gtk.h>

static void
raise_to_top (GtkWidget *button,
              GtkWidget *fixed)
{
  eclipse_fixed_move_above (ECLIPSE_FIXED (fixed), button, NULL);
}

int
main (int argc, char **argv)
{
  GtkWidget *window;
  GtkWidget *fixed;
  GtkWidget *button1, *button2, *button3;
  GtkWidget *label;

  gtk_init (&argc, &argv);
  
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

  fixed = eclipse_fixed_new ();

  /* Add them in upside-down order */
  button1 = gtk_button_new_with_label ("On Top");
  gtk_container_add (GTK_CONTAINER (fixed), button1);
  
  button2 = gtk_button_new_with_label ("In middle");
  gtk_container_add (GTK_CONTAINER (fixed), button2);

  button3 = gtk_button_new_with_label ("On bottom");
  gtk_container_add (GTK_CONTAINER (fixed), button3);

  /* Move them around (ending up in the order the labels say) */
  eclipse_fixed_move_above (ECLIPSE_FIXED (fixed), button1, button2);
  eclipse_fixed_move_below (ECLIPSE_FIXED (fixed), button3, button2);
  eclipse_fixed_move_above (ECLIPSE_FIXED (fixed), button3, button2);
  eclipse_fixed_move_below (ECLIPSE_FIXED (fixed), button3, button1);
  eclipse_fixed_move_above (ECLIPSE_FIXED (fixed), button1, NULL);
  eclipse_fixed_move_below (ECLIPSE_FIXED (fixed), button3, NULL);

  /* Tile them with slight overlap to see stacking order */
  eclipse_fixed_set_location (ECLIPSE_FIXED (fixed), button1, 20, 20);
  eclipse_fixed_set_size (ECLIPSE_FIXED (fixed), button1, 100, 100);

  eclipse_fixed_set_location (ECLIPSE_FIXED (fixed), button2, 100, 20);
  eclipse_fixed_set_size (ECLIPSE_FIXED (fixed), button2, 100, 100);

  eclipse_fixed_set_location (ECLIPSE_FIXED (fixed), button3, 180, 20);
  eclipse_fixed_set_size (ECLIPSE_FIXED (fixed), button3, 100, 100);  

  /* Make clicking buttons restack them */
  gtk_signal_connect (GTK_OBJECT (button1), "clicked",
                      GTK_SIGNAL_FUNC (raise_to_top),
                      fixed);

  gtk_signal_connect (GTK_OBJECT (button2), "clicked",
                      GTK_SIGNAL_FUNC (raise_to_top),
                      fixed);

  gtk_signal_connect (GTK_OBJECT (button3), "clicked",
                      GTK_SIGNAL_FUNC (raise_to_top),
                      fixed);
  
  /* Add some no-window widgets */
  label = gtk_label_new ("Label1");
  gtk_container_add (GTK_CONTAINER (fixed), label);
  eclipse_fixed_set_location (ECLIPSE_FIXED (fixed), label, 20, 150);

  label = gtk_label_new ("Label2");
  gtk_container_add (GTK_CONTAINER (fixed), label);
  eclipse_fixed_set_location (ECLIPSE_FIXED (fixed), label, 20, 180);

  gtk_container_add (GTK_CONTAINER (window), fixed);
  
  gtk_widget_show_all (window);

  gtk_signal_connect (GTK_OBJECT (window), "destroy",
                      GTK_SIGNAL_FUNC (gtk_main_quit),
                      NULL);
  
  gtk_main ();
  
  return 0;
}

#endif

Back to the top