Skip to content
Snippets Groups Projects
zmapWindowContainerGroup.c 30 KiB
Newer Older
rds's avatar
rds committed
/*  File: zmapWindowContainerGroup.c
 *  Author: Roy Storey (rds@sanger.ac.uk)
 *  Copyright (c) 2008: Genome Research Ltd.
 *-------------------------------------------------------------------
 * ZMap is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * or see the on-line version at http://www.gnu.org/copyleft/gpl.txt
 *-------------------------------------------------------------------
 * This file is part of the ZMap genome database package
 * originally written by:
 *
 * 	Ed Griffiths (Sanger Institute, UK) edgrif@sanger.ac.uk,
 *      Roy Storey (Sanger Institute, UK) rds@sanger.ac.uk
 *
 * Description: 
 *
 * Exported functions: See XXXXXXXXXXXXX.h
 * HISTORY:
rds's avatar
rds committed
 * Last edited: Jun  3 22:00 2009 (rds)
rds's avatar
rds committed
 * Created: Wed Dec  3 10:02:22 2008 (rds)
rds's avatar
rds committed
 * CVS info:   $Id: zmapWindowContainerGroup.c,v 1.2 2009-06-03 22:29:08 rds Exp $
rds's avatar
rds committed
 *-------------------------------------------------------------------
 */

rds's avatar
rds committed
#include <zmapWindowCanvas.h>
rds's avatar
rds committed
#include <zmapWindowContainerGroup_I.h>
#include <zmapWindowContainerChildren_I.h>
#include <zmapWindowContainerUtils.h>
#include <zmapWindow_P.h>	/* ITEM_FEATURE_DATA, ITEM_FEATURE_TYPE */
#include <math.h>

enum
  {
    CONTAINER_PROP_0,		/* zero is invalid */
    CONTAINER_PROP_VISIBLE,
    CONTAINER_PROP_COLUMN_REDRAW,
  };


typedef struct
{
  ZMapWindowContainerUpdateHook hook_func;
  gpointer                      hook_data;
} ContainerUpdateHookStruct, *ContainerUpdateHook;


static void zmap_window_container_group_class_init  (ZMapWindowContainerGroupClass container_class);
static void zmap_window_container_group_init        (ZMapWindowContainerGroup      group);
static void zmap_window_container_group_set_property(GObject               *object, 
						     guint                  param_id,
						     const GValue          *value,
						     GParamSpec            *pspec);
static void zmap_window_container_group_get_property(GObject               *object,
						     guint                  param_id,
						     GValue                *value,
						     GParamSpec            *pspec);
static void zmap_window_container_group_destroy     (GObject *object);


static void zmap_window_container_group_draw (FooCanvasItem *item, GdkDrawable *drawable,
					      GdkEventExpose *expose);
static void zmap_window_container_group_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags);
static void zmap_window_container_group_reposition(ZMapWindowContainerGroup container_group, 
						   double rect_x1,   double rect_y1,
						   double rect_x2,   double rect_y2,
						   double *dx_repos, double *dy_repos);

rds's avatar
rds committed
static void maximise_background_rectangle(ZMapWindowContainerGroup this_container, 
					  FooCanvasItem           *container_item,
					  FooCanvasRE             *rect);
static void crop_rectangle_to_scroll_region(gpointer rectangle_data, gpointer points_data);
static void zmap_window_container_scroll_region_get_item_bounds(FooCanvasItem *item,
								double *x1, double *y1,
								double *x2, double *y2);
static void zmap_window_container_update_with_crop(FooCanvasItem *item, 
						   double i2w_dx, double i2w_dy,
						   FooCanvasPoints *itemised_scroll_region,
						   int flags);
static void zmap_window_container_invoke_update_hooks(ZMapWindowContainerGroup container,
						      double x1, double y1, double x2, double y2);
static gint find_update_hook_cb(gconstpointer list_data, gconstpointer query_data);

rds's avatar
rds committed

#ifdef NO_NEED
static FooCanvasGroupClass *group_parent_class_G = NULL;
#endif
static FooCanvasItemClass  *item_parent_class_G  = NULL;


GType zmapWindowContainerGroupGetType(void)
{
  static GType group_type = 0;
  
  if (!group_type) {
    static const GTypeInfo group_info = 
      {
	sizeof (zmapWindowContainerGroupClass),
	(GBaseInitFunc) NULL,
	(GBaseFinalizeFunc) NULL,
	(GClassInitFunc) zmap_window_container_group_class_init,
	NULL,           /* class_finalize */
	NULL,           /* class_data */
	sizeof (zmapWindowContainerGroup),
	0,              /* n_preallocs */
	(GInstanceInitFunc) zmap_window_container_group_init
	
      };
    
    group_type = g_type_register_static (FOO_TYPE_CANVAS_GROUP,
					 ZMAP_WINDOW_CONTAINER_GROUP_NAME,
					 &group_info,
					 0);
  }
  
  return group_type;
}


ZMapWindowContainerGroup zmapWindowContainerGroupCreate(FooCanvasGroup        *parent,
							ZMapContainerLevelType level,
							double                 child_spacing,
							GdkColor              *background_fill_colour,
							GdkColor              *background_border_colour)
{
  ZMapWindowContainerGroup container  = NULL;
  ZMapWindowContainerGroup parent_container  = NULL;
  FooCanvasItem *features   = NULL;
  FooCanvasItem *overlay    = NULL;
  FooCanvasItem *underlay   = NULL;
  FooCanvasItem *background = NULL;
  FooCanvasItem *item;
  FooCanvasGroup *group;
  GType container_type;
  double this_spacing = 200.0;

  if(ZMAP_IS_CONTAINER_GROUP(parent))
    {
      zMapAssertNotReached();
      
      parent = (FooCanvasGroup *)zmapWindowContainerGetFeatures((ZMapWindowContainerGroup)parent);
    }
  else
    {
      if((parent_container = (ZMapWindowContainerGroup)(((FooCanvasItem *)parent)->parent)))
	{
	  this_spacing     = parent_container->child_spacing;
	  level            = parent_container->level + 1;
	}
    }
  
  container_type = ZMAP_TYPE_CONTAINER_GROUP;
  
  switch(level)
    {
    case ZMAPCONTAINER_LEVEL_ROOT:
      container_type = ZMAP_TYPE_CONTAINER_CONTEXT;
      break;
    case ZMAPCONTAINER_LEVEL_ALIGN:
      container_type = ZMAP_TYPE_CONTAINER_ALIGNMENT;
      break;
    case ZMAPCONTAINER_LEVEL_BLOCK:
      container_type = ZMAP_TYPE_CONTAINER_BLOCK;
      break;
    case ZMAPCONTAINER_LEVEL_STRAND:
      container_type = ZMAP_TYPE_CONTAINER_STRAND;
      break;
    case ZMAPCONTAINER_LEVEL_FEATURESET:
      container_type = ZMAP_TYPE_CONTAINER_FEATURESET;
      break;
    default:
      container_type = ZMAP_TYPE_CONTAINER_GROUP;
      break;
    }

  item = foo_canvas_item_new(parent, container_type, 
			     "x", 0.0,
			     "y", 0.0,
			     NULL);
      
  if(item && ZMAP_IS_CONTAINER_GROUP(item))
    {
      group     = FOO_CANVAS_GROUP(item);
      container = ZMAP_CONTAINER_GROUP(item);
      container->level         = level;	/* level */
      container->child_spacing = child_spacing;
      container->this_spacing  = this_spacing;
      container->flags.column_redraw = FALSE;

      background = foo_canvas_item_new(group, ZMAP_TYPE_CONTAINER_BACKGROUND,
				       "original-background", background_fill_colour,
				       NULL);

      underlay   = foo_canvas_item_new(group, ZMAP_TYPE_CONTAINER_UNDERLAY, NULL);

      features   = foo_canvas_item_new(group, ZMAP_TYPE_CONTAINER_FEATURES, NULL);

      overlay    = foo_canvas_item_new(group, ZMAP_TYPE_CONTAINER_OVERLAY,  NULL);

    }
  
  return container;
}


/* Currently this function only works with columns, but the intention
 * is that it could work with blocks and aligns too at some later
 * point in time... */
gboolean zmapWindowContainerSetVisibility(FooCanvasGroup *container_parent, gboolean visible)
{
  gboolean setable = FALSE;     /* Most columns aren't */

  /* We make sure that the container_parent is 
   * - A parent
   * - Is a featureset... This is probably a limit too far.
   */
  if(ZMAP_IS_CONTAINER_GROUP(container_parent))
    {
      if(visible)
	foo_canvas_item_show((FooCanvasItem *)container_parent);
      else
	foo_canvas_item_hide((FooCanvasItem *)container_parent);

      setable = TRUE;
    }

  return setable ;
}

void zmapWindowContainerRequestReposition(ZMapWindowContainerGroup container)
{
  ZMapWindowContainerGroup context_container;

  context_container = zmapWindowContainerUtilsGetParentLevel(container, ZMAPCONTAINER_LEVEL_ROOT);

  if(context_container)
    {
      g_object_set(G_OBJECT(context_container),
		   "need-reposition", TRUE,
		   NULL);
    }

  return ;
}

void zmapWindowContainerGroupBackgroundSize(ZMapWindowContainerGroup container, double height)
{
  container->height = height;

  return ;
}

void zmapWindowContainerGroupChildRedrawRequired(ZMapWindowContainerGroup container, 
						 gboolean redraw_required)
{
  container->flags.column_redraw = redraw_required;

  return ;
}

void zmapWindowContainerGroupSetBackgroundColour(ZMapWindowContainerGroup container,
						 GdkColor *new_colour)
{
  ZMapWindowContainerBackground background;

  if((background = zmapWindowContainerGetBackground(container)))
    zmapWindowContainerBackgroundSetColour(background, new_colour);

  return ;
}

void zmapWindowContainerGroupResetBackgroundColour(ZMapWindowContainerGroup container)
{
  ZMapWindowContainerBackground background;

  if((background = zmapWindowContainerGetBackground(container)))
    zmapWindowContainerBackgroundResetColour(background);

  return ;
}

void zmapWindowContainerGroupAddUpdateHook(ZMapWindowContainerGroup container,
					   ZMapWindowContainerUpdateHook hook,
					   gpointer user_data)
{
  ContainerUpdateHook update_hook = NULL;

  if((update_hook = g_new0(ContainerUpdateHookStruct, 1)))
    {
      update_hook->hook_func = hook;
      update_hook->hook_data = user_data;

      container->update_hooks = g_slist_append(container->update_hooks, 
					       update_hook);
    }

  return ;
}

rds's avatar
rds committed
void zmapWindowContainerGroupRemoveUpdateHook(ZMapWindowContainerGroup container,
					      ZMapWindowContainerUpdateHook hook,
					      gpointer user_data)
{
  ContainerUpdateHookStruct update_hook = {NULL};
  GSList *slist = NULL;

  update_hook.hook_func = hook;
  update_hook.hook_data = user_data;

  if((slist = g_slist_find_custom(container->update_hooks,
				  &update_hook,
				  find_update_hook_cb)))
    {
      g_free(slist->data);
      container->update_hooks = g_slist_remove(container->update_hooks, slist->data);
    }
}

rds's avatar
rds committed
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
ZMapWindowContainerGroup zmapWindowContainerGroupDestroy(ZMapWindowContainerGroup container)
{
  g_object_unref(G_OBJECT(container));

  container = NULL;

  return container;
}















/* object impl */
static void zmap_window_container_group_class_init  (ZMapWindowContainerGroupClass container_class)
{
  ZMapWindowContainerGroupClass canvas_class;
  FooCanvasItemClass *item_class;
  GObjectClass *gobject_class;
  GParamSpec *param_spec;

  gobject_class = (GObjectClass *) container_class;
  canvas_class  = (ZMapWindowContainerGroupClass)container_class;
  item_class    = (FooCanvasItemClass *)container_class;

  gobject_class->set_property = zmap_window_container_group_set_property;
  gobject_class->get_property = zmap_window_container_group_get_property;

  if((param_spec = g_object_class_find_property(gobject_class, "visible")))
    {
      g_object_class_override_property(gobject_class, CONTAINER_PROP_VISIBLE,
				       g_param_spec_get_name(param_spec));
    }

  g_object_class_install_property(gobject_class, CONTAINER_PROP_COLUMN_REDRAW,
				  g_param_spec_boolean("column-redraw", "column redraw",
						       "Column needs redrawing when zoom changes",
						       FALSE, ZMAP_PARAM_STATIC_RW));

  item_parent_class_G  = (FooCanvasItemClass *)(g_type_class_peek_parent(container_class));

  zMapAssert(item_parent_class_G);
  zMapAssert(item_parent_class_G->update);

  item_class->draw     = zmap_window_container_group_draw;
  item_class->update   = zmap_window_container_group_update;

  container_class->reposition_group = zmap_window_container_group_reposition;

  gobject_class->dispose = zmap_window_container_group_destroy;

  return ;
}

static void zmap_window_container_group_init        (ZMapWindowContainerGroup container)
{

  g_object_set_data(G_OBJECT(container), CONTAINER_TYPE_KEY, GINT_TO_POINTER(CONTAINER_GROUP_PARENT));

  return ;
}

static void zmap_window_container_group_set_property(GObject               *object, 
						     guint                  param_id,
						     const GValue          *value,
						     GParamSpec            *pspec)

{
  ZMapWindowContainerGroup container;

  g_return_if_fail(ZMAP_IS_CONTAINER_GROUP(object));

  container = ZMAP_CONTAINER_GROUP(object);

  switch(param_id)
    {
    case CONTAINER_PROP_VISIBLE:
      {
	gboolean visible = FALSE;

	visible = g_value_get_boolean(value);
	switch(container->level)
	  {
	  case ZMAPCONTAINER_LEVEL_FEATURESET:
	    if(visible)
	      {
		foo_canvas_item_show(FOO_CANVAS_ITEM(object));
	      }
	    else
	      {
		foo_canvas_item_hide(FOO_CANVAS_ITEM(object));
	      }
	    break;
	  default:
	    break;
	  } /* switch(container->level) */
      }
      break;
    case CONTAINER_PROP_COLUMN_REDRAW:
      {
	switch(container->level)
	  {
	  case ZMAPCONTAINER_LEVEL_FEATURESET:
	    container->flags.column_redraw = g_value_get_boolean(value);
	    break;
	  default:
	    break;
	  } /* switch(container->level) */
      }
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
      break;
    } /* switch(param_id) */

  return ;
}

static void zmap_window_container_group_get_property(GObject               *object,
						     guint                  param_id,
						     GValue                *value,
						     GParamSpec            *pspec)
{

  switch(param_id)
    {
    case CONTAINER_PROP_VISIBLE:
      {
	FooCanvasItem *item;
	item = FOO_CANVAS_ITEM(object);
	g_value_set_boolean (value, item->object.flags & FOO_CANVAS_ITEM_VISIBLE);
      }
      break;
    case CONTAINER_PROP_COLUMN_REDRAW:
      {
	ZMapWindowContainerGroup container;

	container = ZMAP_CONTAINER_GROUP(object);

	switch(container->level)
	  {
	  case ZMAPCONTAINER_LEVEL_FEATURESET:
	    g_value_set_boolean(value, container->flags.column_redraw);
	    break;
	  default:
	    break;
	  } /* switch(container->level) */
      }
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
      break;
    } /* switch(param_id) */

  return ;
}
#ifdef POINT_REQUIRED
static double window_container_group_invoke_point (FooCanvasItem *item, 
						      double x, double y, 
						      int cx, int cy,
						      FooCanvasItem **actual_item)
{
  /* Calculate x & y in item local coordinates */
  
  if (FOO_CANVAS_ITEM_GET_CLASS (item)->point)
    return FOO_CANVAS_ITEM_GET_CLASS (item)->point (item, x, y, cx, cy, actual_item);
  
  return 1e18;
}


/* Point handler for canvas groups */
static double zmap_window_container_group_item_point (FooCanvasItem *item, 
						      double x,  double y, 
						      int    cx, int    cy,
						      FooCanvasItem **actual_item)
{
  FooCanvasGroup *group;
  FooCanvasItem *child, *point_item;
  GList *list = NULL;
  int x1, y1, x2, y2;
  double gx, gy;
  double dist, best;

  group = FOO_CANVAS_GROUP (item);
  
  x1 = cx - item->canvas->close_enough;
  y1 = cy - item->canvas->close_enough;
  x2 = cx + item->canvas->close_enough;
  y2 = cy + item->canvas->close_enough;
  
  best = 0.0;
  *actual_item = NULL;
  
  gx = x - group->xpos;
  gy = y - group->ypos;

  dist = 0.0; /* keep gcc happy */

  list = group->item_list;

  while(list)
    {
      child = list->data;

      if ((child->object.flags & FOO_CANVAS_ITEM_MAPPED)
	  && FOO_CANVAS_ITEM_GET_CLASS (child)->point) 
	{
	  dist = window_container_group_invoke_point (child, gx, gy, cx, cy, &point_item);
	  if(point_item && ((int)(dist * item->canvas->pixels_per_unit_x + 0.5) <= item->canvas->close_enough) &&
	     (dist <= best))
	    {
	      best = dist;
	      *actual_item = point_item;
	    }
	} 
      list = list->next;
    }
  
  if(actual_item == NULL && item_parent_class_G->point)
    best = (item_parent_class_G->point)(item, x, y, cx, cy, actual_item);

  return best;
}
#endif

static void zmap_window_container_group_destroy     (GObject *object)
{
  ZMapWindowContainerGroup container;
  GObjectClass *object_class;

  object_class = (GObjectClass *)item_parent_class_G;
  container = (ZMapWindowContainerGroup)object;

  
  g_slist_foreach(container->update_hooks, (GFunc)g_free, NULL);
  g_slist_free(container->update_hooks);
  container->update_hooks = NULL;

  if(object_class->dispose)
    (object_class->dispose)(object);

  return ;
}


static void zmap_window_container_group_draw (FooCanvasItem *item, GdkDrawable *drawable,
					      GdkEventExpose *expose)
{
  if(item_parent_class_G->draw)
    (item_parent_class_G->draw)(item, drawable, expose);

  return ;
}

static void maximise_background_rectangle(ZMapWindowContainerGroup this_container, 
					  FooCanvasItem           *container_item,
					  FooCanvasRE             *rect)
{
  FooCanvasItem *rect_item;
  double irx1, irx2, iry1, iry2;
  int container_x2, container_y2; /* container canvas coords, calculated from group->update above. */
  
  irx1 = iry1 = 0.0;	/* placed @ 0,0 */
  
  /* We can't trust item->x1 and item->y1 as empty child
   * groups return 0,0->0,0 hence extend all parents to
   * 0,0! */
  container_x2 = (int)(container_item->x2);
  container_y2 = (int)(container_item->y2);

  rect_item = (FooCanvasItem *)rect; /*  */

  foo_canvas_item_i2w(rect_item, &irx1, &iry1);
  foo_canvas_c2w(rect_item->canvas, container_x2, container_y2, &irx2, &iry2);

  if((iry2 - iry1 + 1) < this_container->height)
    iry2 = this_container->height + iry1;
  
  rect->x1 = irx1;
  rect->y1 = iry1;
  rect->x2 = irx2;
  rect->y2 = iry2;

  if(rect->x1 == rect->x2)
    rect->x2 = rect->x1 + this_container->this_spacing;

  foo_canvas_item_w2i(rect_item, &(rect->x1), &(rect->y1));
  foo_canvas_item_w2i(rect_item, &(rect->x2), &(rect->y2));

  /* rect->x1 & rect->y1 should == 0.0 */

  return ;
}

static void crop_rectangle_to_scroll_region(gpointer rectangle_data, gpointer points_data)
{
  FooCanvasRE *rect;
  FooCanvas *foo_canvas;
  FooCanvasItem *crop_item;
  FooCanvasPoints *scroll_region = (FooCanvasPoints *)points_data;
  double scroll_x1, scroll_y1, scroll_x2, scroll_y2;
  double iwx1, iwy1, iwx2, iwy2;
  
  rect       = (FooCanvasRE *)rectangle_data;
  crop_item  = (FooCanvasItem *)rect;
  foo_canvas = crop_item->canvas;

  iwx1 = rect->x1;
  iwy1 = rect->y1;
  iwx2 = rect->x2;
  iwy2 = rect->y2;
  
  if((scroll_region == NULL))
    {
      /* x unused ATM */
      scroll_x1 = foo_canvas->scroll_x1;
      scroll_x2 = foo_canvas->scroll_x2;
      
      scroll_y1 = foo_canvas->scroll_y1;
      scroll_y2 = foo_canvas->scroll_y2;
      
      foo_canvas_item_w2i(crop_item, &scroll_x1, &scroll_y1);
      foo_canvas_item_w2i(crop_item, &scroll_x2, &scroll_y2);
    }
  else
    {
      scroll_x1 = scroll_region->coords[0];
      scroll_y1 = scroll_region->coords[1];
      scroll_x2 = scroll_region->coords[2];
      scroll_y2 = scroll_region->coords[3];
    }

  if (!(iwy2 < scroll_y1) && !(iwy1 > scroll_y2) && ((iwy1 < scroll_y1) || (iwy2 > scroll_y2)))
    {
      if(iwy1 < scroll_y1)
	{
	  rect->y1 = scroll_y1 - 1.0;
	}
      
      if(iwy2 > scroll_y2)
	{
	  rect->y2 = scroll_y2 + 1.0;
	}
    }

  return ;
}

static void zmap_window_container_scroll_region_get_item_bounds(FooCanvasItem *item,
								double *x1, double *y1,
								double *x2, double *y2)
{
  FooCanvas *foo_canvas;
  double scroll_x1, scroll_y1, scroll_x2, scroll_y2;

  foo_canvas = item->canvas;

  scroll_x1 = foo_canvas->scroll_x1;
  scroll_x2 = foo_canvas->scroll_x2;

  scroll_y1 = foo_canvas->scroll_y1;
  scroll_y2 = foo_canvas->scroll_y2;
  
  foo_canvas_item_w2i(item, &scroll_x1, &scroll_y1);
  foo_canvas_item_w2i(item, &scroll_x2, &scroll_y2);
  
  if(x1)
    *x1 = scroll_x1;
  if(y1)
    *y1 = scroll_y1;
  if(x2)
    *x2 = scroll_x2;
  if(y2)
    *y2 = scroll_y2;

  return ;
}

rds's avatar
rds committed
/* container update flags */
enum
  {
    CONTAINER_UPDATE_CROP_REQUIRED = 1 << 3,
  };

rds's avatar
rds committed
static void zmap_window_container_update_with_crop(FooCanvasItem *item, 
						   double i2w_dx, double i2w_dy,
						   FooCanvasPoints *itemised_scroll_region,
						   int flags)
{
  if(FOO_IS_CANVAS_GROUP(item))
    {
      FooCanvasGroup *group;
      GList *list;

      group = (FooCanvasGroup *)item;

      if((list = group->item_list))
	{
	  double sub_i2w_dx, sub_i2w_dy;

	  sub_i2w_dx = i2w_dx + group->xpos;
	  sub_i2w_dy = i2w_dx + group->ypos;

	  itemised_scroll_region->coords[0] -= group->xpos;
	  itemised_scroll_region->coords[1] -= group->ypos;
	  itemised_scroll_region->coords[2] -= group->xpos;
	  itemised_scroll_region->coords[3] -= group->ypos;

	  do
	    {
	      zmap_window_container_update_with_crop((FooCanvasItem *)(list->data), 
						     sub_i2w_dx, sub_i2w_dy,
						     itemised_scroll_region, flags);
	    }
	  while((list = list->next));

	  itemised_scroll_region->coords[0] += group->xpos;
	  itemised_scroll_region->coords[1] += group->ypos;
	  itemised_scroll_region->coords[2] += group->xpos;
	  itemised_scroll_region->coords[3] += group->ypos;

	}
    }
rds's avatar
rds committed
  else if(flags & CONTAINER_UPDATE_CROP_REQUIRED)
rds's avatar
rds committed
    {
      if(FOO_IS_CANVAS_RE(item))
	crop_rectangle_to_scroll_region((FooCanvasRE *)item, NULL);
    }

  if(FOO_CANVAS_ITEM_GET_CLASS(item)->update)
    (FOO_CANVAS_ITEM_GET_CLASS(item)->update)(item, i2w_dx, i2w_dy, flags);

  return ;
}

static void zmap_window_container_invoke_update_hooks(ZMapWindowContainerGroup container,
						      double x1, double y1, double x2, double y2)
{
  if(container->update_hooks)
    {
      FooCanvasPoints bounds;
      double coords[4] = {0.0};
      GSList *hooks;
      
      hooks = container->update_hooks;

      coords[0] = x1;
      coords[1] = y1;
      coords[2] = x2;
      coords[3] = y2;
      
      bounds.coords     = &coords[0];
      bounds.ref_count  = 1;
      bounds.num_points = 2;
      
      do
	{
	  ContainerUpdateHook update_hook;
	  
	  update_hook = (ContainerUpdateHook)(hooks->data);
	  
	  if(update_hook->hook_func)
	    (update_hook->hook_func)(container, &bounds, container->level, update_hook->hook_data);
	}
      while((hooks = hooks->next));
    }

  return ;
}

/* This takes care of the x positioning of the containers as well as the maximising in the y coords. */
static void zmap_window_container_group_update (FooCanvasItem *item, double i2w_dx, double i2w_dy, int flags)
{
  ZMapWindowContainerGroup   this_container = NULL;
  ZMapWindowContainerGroup parent_container = NULL;
  ZMapWindowContainerOverlay   overlay = NULL;
  ZMapWindowContainerUnderlay underlay = NULL;
  FooCanvasRE *rect = NULL;
  FooCanvasItem *parent_parent = NULL;
  FooCanvasGroup *canvas_group;
  GList *item_list;
  double current_x = 0.0;
  double current_y = 0.0;
  gboolean item_visible;
  gboolean doing_reposition;
rds's avatar
rds committed
  gboolean need_cropping;
rds's avatar
rds committed
  gboolean add_strand_border = FALSE;

  canvas_group   = (FooCanvasGroup *)item;
  item_visible   = ((item->object.flags & FOO_CANVAS_ITEM_VISIBLE) == FOO_CANVAS_ITEM_VISIBLE);

  this_container = (ZMapWindowContainerGroup)item;
  this_container->reposition_x = current_x;
  this_container->reposition_y = current_y;

  /* This was in the previous version of the code, copying across... */
  if(add_strand_border && this_container->level == ZMAPCONTAINER_LEVEL_STRAND)
    this_container->reposition_x += this_container->child_spacing;

  if(item->parent && (parent_parent = item->parent->parent))
    {
      parent_container = (ZMapWindowContainerGroup)parent_parent;
rds's avatar
rds committed
#ifdef ACTUALLY_FLAGS_SUBVERSION_IS_BETTER
rds's avatar
rds committed
      /* we could subvert flags parameter, but this is slightly
       * better.  Needs to be propgated through the tree. */
      if(!this_container->flags.need_reposition)
	this_container->flags.need_reposition = parent_container->flags.need_reposition;

rds's avatar
rds committed
      this_container->flags.need_cropping = parent_container->flags.need_cropping;
#endif
rds's avatar
rds committed
      current_x = parent_container->reposition_x;
      current_y = parent_container->reposition_y;
    }

rds's avatar
rds committed
  doing_reposition = ((flags & ZMAP_CANVAS_UPDATE_NEED_REPOSITION) == ZMAP_CANVAS_UPDATE_NEED_REPOSITION);
  need_cropping    = ((flags & ZMAP_CANVAS_UPDATE_CROP_REQUIRED)   == ZMAP_CANVAS_UPDATE_CROP_REQUIRED);
rds's avatar
rds committed

  if(doing_reposition)
    {
      GList *list, *list_end, tmp_features = {NULL}, tmp_background = {NULL}; 
      gboolean print_debug = FALSE;

      if((item_list = canvas_group->item_list))
	{
	  /* reposition immediate descendants (features, background overlay, underlay) */
	  do
	    {
	      if(FOO_IS_CANVAS_GROUP(item_list->data))
		{
		  FooCanvasGroup *group = (FooCanvasGroup *)(item_list->data);
		  
		  if(group->xpos != 0.0)
		    group->xpos = 0.0;
		  if(group->ypos != 0.0)
		    group->ypos = 0.0;
		  
		  if(ZMAP_IS_CONTAINER_OVERLAY(item_list->data))
		    overlay = (ZMapWindowContainerOverlay)(item_list->data);
		  else if(ZMAP_IS_CONTAINER_UNDERLAY(item_list->data))
		    underlay = (ZMapWindowContainerUnderlay)(item_list->data);
		}
	      else if(ZMAP_IS_CONTAINER_BACKGROUND(item_list->data))
		{
		  rect = FOO_CANVAS_RE(item_list->data);
		  
		  if(rect->x1 != 0.0)
		    rect->x1 = 0.0;
		  if(rect->y1 != 0.0)
		    rect->y1 = 0.0;
		  
		  rect->x2 = 1.0;	/* There's no way to know width */
		  rect->y2 = this_container->height; /* We know height though. */
		}
	      /* no recursion here... */
	    }
	  while((item_list = item_list->next));
	}


      if(print_debug)
	{
	  switch(this_container->level)
	    {
	    case ZMAPCONTAINER_LEVEL_ROOT:       printf("context: ");    break;
	    case ZMAPCONTAINER_LEVEL_ALIGN:      printf("align: ");      break;
	    case ZMAPCONTAINER_LEVEL_BLOCK:      printf("block: ");      break;
	    case ZMAPCONTAINER_LEVEL_STRAND:     printf("strand: ");     break;
	    case ZMAPCONTAINER_LEVEL_FEATURESET: printf("featureset: "); break;
	    default:
	      break;
	    }
	  
	  printf("current_x=%f, current_y=%f\n", current_x, current_y);
	}

      if(item_visible)
	{
	  FooCanvasGroup *real_group;

	  real_group       = (FooCanvasGroup *)this_container;

	  /* There's _no_ need to use group->translate, nor move by dx,dy. Just set the positions */
	  real_group->xpos = current_x;
	  /* We don't do y at the moment. no real idea what should happen here. */
	  /* real_group->ypos = current_y; */
	}

      /* We _only_ update the background and features at this time. Underlays and overlays will get done later */
      tmp_background.next = &tmp_features;
      tmp_background.data = zmapWindowContainerGetBackground(this_container);
      tmp_background.prev = NULL;

      tmp_features.next = NULL;
      tmp_features.data = zmapWindowContainerGetFeatures(this_container);
      tmp_features.prev = &tmp_background;

      list     = canvas_group->item_list;
      list_end = canvas_group->item_list_end;

      canvas_group->item_list     = &tmp_background;
      canvas_group->item_list_end = &tmp_features;

      (item_parent_class_G->update)(item, i2w_dx, i2w_dy, flags);

      canvas_group->item_list     = list;
      canvas_group->item_list_end = list_end;

    }
  else
    {
      (item_parent_class_G->update)(item, i2w_dx, i2w_dy, flags);
    }

  if(rect && item_visible)
    {
rds's avatar
rds committed
      gboolean need_2nd_update = TRUE;
rds's avatar
rds committed

      if(doing_reposition)
	{
	  maximise_background_rectangle(this_container, item, rect);

	  /* Update the current reposition_x, and reposition_y coords */
	  if(parent_container)
	    {
	      double dx, dy;
	      
	      if(ZMAP_CONTAINER_GROUP_GET_CLASS(this_container)->reposition_group)
		(ZMAP_CONTAINER_GROUP_GET_CLASS(this_container)->reposition_group)(this_container, 
										   rect->x1, rect->y1, 
										   rect->x2, rect->y2, 
										   &dx, &dy);
	      
	      parent_container->reposition_x += dx;
	      parent_container->reposition_y += dy;
	    }

	  zmap_window_container_invoke_update_hooks(this_container, 
						    rect->x1, rect->y1,
						    rect->x2, rect->y2);
	}

      /* The background needs updating now so that the canvas knows
       * where it is (canvas coords) for events. We are only setting
       * the points to match the containers bounds or within so no
       * need to re-update the whole tree...phew... */
rds's avatar
rds committed
      if(need_2nd_update || need_cropping)
rds's avatar
rds committed
	{
	  FooCanvasItem *update_items[3] = {NULL};
	  FooCanvasPoints scroll_region;
	  double coords[4];
rds's avatar
rds committed
	  int i = 0, update_flags = flags;
rds's avatar
rds committed

	  i2w_dx += canvas_group->xpos;
	  i2w_dy += canvas_group->ypos;

	  update_items[i++] = (FooCanvasItem *)rect;
rds's avatar
rds committed
	  update_items[i++] = (FooCanvasItem *)overlay;
	  update_items[i++] = (FooCanvasItem *)underlay;

	  if(need_cropping)
	    update_flags |= CONTAINER_UPDATE_CROP_REQUIRED;