/* $Id: guppi-multiview.c,v 1.8 2001/07/10 02:53:26 trow Exp $ */

/* guppi-multiview.c
 *
 * Copyright (C) 2000  Jonathan Blandford
 *
 * Modified for use in Guppi by Jon Trowbridge
 *
 * 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.
 */

#include <config.h>
#include "guppi-multiview.h"

/* #include <gtk/gtk.h> */
#include "guppi-memory.h"

static void guppi_multiview_init (GuppiMultiview * multiview);
static void guppi_multiview_class_init (GuppiMultiviewClass * klass);
static void guppi_multiview_size_request (GtkWidget * widget,
					  GtkRequisition * requisition);
static void guppi_multiview_size_allocate (GtkWidget * widget,
					   GtkAllocation * allocation);
static void guppi_multiview_map (GtkWidget * widget);
static void guppi_multiview_unmap (GtkWidget * widget);
static int guppi_multiview_expose (GtkWidget * widget,
				   GdkEventExpose * event);
static GtkType guppi_multiview_child_type (GtkContainer * container);
static void guppi_multiview_forall (GtkContainer * container,
				    gboolean include_internals,
				    GtkCallback callback,
				    gpointer callback_data);
static void guppi_multiview_add (GtkContainer * widget, GtkWidget * child);
static void guppi_multiview_remove (GtkContainer * widget, GtkWidget * child);


static GtkContainerClass *parent_class = NULL;

GtkType
guppi_multiview_get_type (void)
{
  static GtkType multiview_type = 0;

  if (!multiview_type) {
    static const GtkTypeInfo multiview_info = {
      "GuppiMultiview",
      sizeof (GuppiMultiview),
      sizeof (GuppiMultiviewClass),
      (GtkClassInitFunc) guppi_multiview_class_init,
      (GtkObjectInitFunc) guppi_multiview_init,
      /* reserved_1 */ NULL,
      /* reserved_2 */ NULL,
      (GtkClassInitFunc) NULL,
    };

    multiview_type =
      gtk_type_unique (gtk_container_get_type (), &multiview_info);
  }

  return multiview_type;
}

static void
guppi_multiview_init (GuppiMultiview * multiview)
{
  GTK_WIDGET_SET_FLAGS (GTK_WIDGET (multiview), GTK_NO_WINDOW);

  multiview->current = NULL;
  multiview->children = NULL;
}

static void
finalize (GtkObject *obj)
{
  guppi_finalized (obj);

  if (GTK_OBJECT_CLASS (parent_class)->finalize)
    GTK_OBJECT_CLASS (parent_class)->finalize (obj);
}

static void
guppi_multiview_class_init (GuppiMultiviewClass * klass)
{
  GtkObjectClass *object_class;
  GtkWidgetClass *widget_class;
  GtkContainerClass *container_class;

  object_class = (GtkObjectClass *) klass;
  widget_class = (GtkWidgetClass *) klass;
  container_class = (GtkContainerClass *) klass;
  parent_class = gtk_type_class (gtk_container_get_type ());

  object_class->finalize = finalize;

  widget_class->size_request = guppi_multiview_size_request;
  widget_class->size_allocate = guppi_multiview_size_allocate;
  widget_class->map = guppi_multiview_map;
  widget_class->unmap = guppi_multiview_unmap;
  widget_class->expose_event = guppi_multiview_expose;

  container_class->forall = guppi_multiview_forall;
  container_class->add = guppi_multiview_add;
  container_class->remove = guppi_multiview_remove;
  container_class->child_type = guppi_multiview_child_type;
}

static void
guppi_multiview_size_request (GtkWidget * widget,
			      GtkRequisition * requisition)
{
  GList *tmp_list;
  GuppiMultiview *multiview;
  GtkRequisition child_requisition;
  GtkWidget *child;

  multiview = GUPPI_MULTIVIEW (widget);

  requisition->width = 0;
  requisition->height = 0;
  /* We find the maximum size of all children widgets */
  tmp_list = multiview->children;
  while (tmp_list) {
    child = GTK_WIDGET (tmp_list->data);
    tmp_list = tmp_list->next;

    if (GTK_WIDGET_VISIBLE (child)) {
      gtk_widget_size_request (child, &child_requisition);
      requisition->width = MAX (requisition->width, child_requisition.width);
      requisition->height =
	MAX (requisition->height, child_requisition.height);
      if (GTK_WIDGET_MAPPED (child) && child != multiview->current) {
	gtk_widget_unmap (GTK_WIDGET (child));
      }
    }
  }
}
static void
guppi_multiview_size_allocate (GtkWidget * widget, GtkAllocation * allocation)
{
  GuppiMultiview *multiview;
  GList *tmp_list;
  GtkWidget *child;

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

  multiview = GUPPI_MULTIVIEW (widget);
  widget->allocation = *allocation;

  tmp_list = multiview->children;
  while (tmp_list) {
    child = GTK_WIDGET (tmp_list->data);
    tmp_list = tmp_list->next;

    if (GTK_WIDGET_VISIBLE (child)) {
      gtk_widget_size_allocate (child, allocation);
    }
  }
}

static void
guppi_multiview_map (GtkWidget * widget)
{
  GuppiMultiview *multiview;

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

  multiview = GUPPI_MULTIVIEW (widget);
  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);

  if (multiview->current &&
      GTK_WIDGET_VISIBLE (multiview->current) &&
      !GTK_WIDGET_MAPPED (multiview->current)) {
    gtk_widget_map (GTK_WIDGET (multiview->current));
  }
}

static void
guppi_multiview_unmap (GtkWidget * widget)
{
  GuppiMultiview *multiview;

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

  multiview = GUPPI_MULTIVIEW (widget);
  GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);

  if (multiview->current &&
      GTK_WIDGET_VISIBLE (multiview->current) &&
      GTK_WIDGET_MAPPED (multiview->current)) {
    gtk_widget_unmap (GTK_WIDGET (multiview->current));
  }
}

static gint
guppi_multiview_expose (GtkWidget * widget, GdkEventExpose * event)
{
  GuppiMultiview *multiview;
  GtkWidget *child;
  GList *tmp_list;
  GdkEventExpose child_event;

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

  if (GTK_WIDGET_DRAWABLE (widget)) {
    multiview = GUPPI_MULTIVIEW (widget);
    child_event = *event;

    tmp_list = multiview->children;
    while (tmp_list) {
      child = GTK_WIDGET (tmp_list->data);
      tmp_list = tmp_list->next;

      if (GTK_WIDGET_DRAWABLE (child) && GTK_WIDGET_NO_WINDOW (child)) {
	gtk_widget_event (child, (GdkEvent *) event);
      }
    }
  }
  return FALSE;
}

static GtkType
guppi_multiview_child_type (GtkContainer * container)
{
  return gtk_widget_get_type ();
}

static void
guppi_multiview_forall (GtkContainer * container,
			gboolean include_internals,
			GtkCallback callback, gpointer callback_data)
{
  GtkWidget *child;
  GuppiMultiview *multiview;
  GList *tmp_list;

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

  multiview = GUPPI_MULTIVIEW (container);

  tmp_list = multiview->children;
  while (tmp_list) {
    child = tmp_list->data;
    tmp_list = tmp_list->next;
    (*callback) (GTK_WIDGET (child), callback_data);
  }
}

static void
guppi_multiview_add (GtkContainer * container, GtkWidget * child)
{
  g_return_if_fail (container != NULL);
  g_return_if_fail (GUPPI_IS_MULTIVIEW (container));
  g_return_if_fail (child != NULL);
  g_return_if_fail (GTK_IS_WIDGET (child));

  guppi_multiview_append_child (GUPPI_MULTIVIEW (container), child);
}

static void
guppi_multiview_remove (GtkContainer * container, GtkWidget * child)
{
  GuppiMultiview *multiview;
  GList *list;

  g_return_if_fail (container != NULL);
  g_return_if_fail (GUPPI_IS_MULTIVIEW (container));
  g_return_if_fail (child != NULL);

  multiview = GUPPI_MULTIVIEW (container);

  list = g_list_find (multiview->children, child);
  g_return_if_fail (list != NULL);

  /* If we are mapped and visible, we want to deal with changing the page. */
  if ((GTK_WIDGET_MAPPED (GTK_WIDGET (container))) &&
      (list->data == (gpointer) multiview->current)) {
    if (list->next != NULL)
      guppi_multiview_set_current (multiview, GTK_WIDGET (list->next->data));
    else
      multiview->current = NULL;
  }

  multiview->children = g_list_remove (multiview->children, child);
  gtk_widget_unparent (child);
}

/* Public Functions */
GtkWidget *
guppi_multiview_new (void)
{
  return GTK_WIDGET (guppi_type_new (guppi_multiview_get_type ()));
}

void
guppi_multiview_prepend_child (GuppiMultiview * multiview, GtkWidget * child)
{
  g_return_if_fail (multiview != NULL);
  g_return_if_fail (GUPPI_IS_MULTIVIEW (multiview));
  g_return_if_fail (child != NULL);
  g_return_if_fail (GTK_IS_WIDGET (child));

  guppi_multiview_insert_child (multiview, NULL, child);
}

void
guppi_multiview_insert_child (GuppiMultiview * multiview,
			      GtkWidget * back_child, GtkWidget * child)
{
  GList *list;

  g_return_if_fail (multiview != NULL);
  g_return_if_fail (GUPPI_IS_MULTIVIEW (multiview));
  g_return_if_fail (child != NULL);
  g_return_if_fail (GTK_IS_WIDGET (child));

  list = g_list_find (multiview->children, back_child);
  if (list == NULL) {
    multiview->children = g_list_prepend (multiview->children, child);
  } else {
    GList *new_el = g_list_alloc ();

    new_el->next = list->next;
    new_el->prev = list;
    if (new_el->next)
      new_el->next->prev = new_el;
    new_el->prev->next = new_el;
    new_el->data = (gpointer) child;
  }
  gtk_widget_set_parent (GTK_WIDGET (child), GTK_WIDGET (multiview));

  if (GTK_WIDGET_REALIZED (GTK_WIDGET (multiview)))
    gtk_widget_realize (GTK_WIDGET (child));

  if (GTK_WIDGET_VISIBLE (GTK_WIDGET (multiview))
      && GTK_WIDGET_VISIBLE (GTK_WIDGET (child))) {
    if (GTK_WIDGET_MAPPED (GTK_WIDGET (child)))
      gtk_widget_unmap (GTK_WIDGET (child));
    gtk_widget_queue_resize (GTK_WIDGET (multiview));
  }

  /* if it's the first and only entry, we want to bring it to the foreground. */
  if (multiview->children->next == NULL)
    guppi_multiview_set_current (multiview, child);
}

void
guppi_multiview_append_child (GuppiMultiview * multiview, GtkWidget * child)
{
  GList *list;

  g_return_if_fail (multiview != NULL);
  g_return_if_fail (GUPPI_IS_MULTIVIEW (multiview));
  g_return_if_fail (child != NULL);
  g_return_if_fail (GTK_IS_WIDGET (child));

  list = g_list_last (multiview->children);
  if (list) {
    guppi_multiview_insert_child (multiview, GTK_WIDGET (list->data), child);
  } else {
    guppi_multiview_insert_child (multiview, NULL, child);
  }
}

gboolean
guppi_multiview_contains (GuppiMultiview * multiview, GtkWidget * child)
{
  g_return_val_if_fail (multiview != NULL, FALSE);
  g_return_val_if_fail (GUPPI_IS_MULTIVIEW (multiview), FALSE);
  g_return_val_if_fail (child != NULL, FALSE);
  g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);

  return g_list_find (multiview->children, child) != NULL;
}

void
guppi_multiview_set_current (GuppiMultiview * multiview, GtkWidget * child)
{
  GList *list;
  GtkWidget *old = NULL;

  g_return_if_fail (multiview != NULL);
  g_return_if_fail (GUPPI_IS_MULTIVIEW (multiview));
  g_return_if_fail (child != NULL);
  g_return_if_fail (GTK_IS_WIDGET (child));

  if (multiview->current == child)
    return;

  list = g_list_find (multiview->children, child);
  g_return_if_fail (list != NULL);

  if ((multiview->current) &&
      (GTK_WIDGET_VISIBLE (multiview->current)) &&
      (GTK_WIDGET_MAPPED (multiview))) {
    old = GTK_WIDGET (multiview->current);
  }

  multiview->current = GTK_WIDGET (list->data);
  if (GTK_WIDGET_VISIBLE (multiview->current) &&
      (GTK_WIDGET_MAPPED (multiview))) {
    gtk_widget_map (multiview->current);
  }
  if (old && GTK_WIDGET_MAPPED (old))
    gtk_widget_unmap (old);

  gtk_widget_show_all (multiview->current);
}


syntax highlighted by Code2HTML, v. 0.9.1