/**********************************************************************
This file is part of Crack dot Com's free source code release of Golgotha.
for information about compiling & licensing issues visit this URL
 If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/

#include "window/window.hh"
#include "area/rectlist.hh"
#include "device/kernel.hh"
#include "error/error.hh"
#include "window/win_evt.hh"
#include "image/image.hh"
#include "window/cursor.hh"
#include "time/profile.hh"

i4_profile_class pf_child_draw_prepare("window::child_prepare"),
  pf_parent_draw_prepare("window::parent_prepare");

#include 

i4_parent_window_class *i4_window_class::root_window()
{
  if (parent)
    return parent->root_window();
  else return 0;
}

i4_parent_window_class *i4_parent_window_class::root_window()
{
  if (parent)
    return parent->root_window();
  else return this;
}

i4_bool i4_window_class::isa_parent(i4_window_class *who)
{
  if (who==0)
    return i4_F;
  else if (who==parent)
    return i4_T;
  else if (parent)
    return parent->isa_parent(who);
  else return i4_F;
}

void i4_window_class::receive_event(i4_event *ev)
{
  if (ev->type()==i4_event::MOUSE_MOVE)
  {
    CAST_PTR(move, i4_mouse_move_event_class, ev);
    mouse_x=move->x;
    mouse_y=move->y;
  }

  if (cursor && parent)  // if we have a cursor we need to load when we get a mouse focus
  {
    if (ev->type()==i4_event::WINDOW_MESSAGE)
    {
      CAST_PTR(wev,i4_window_message_class,ev);
      if (wev->sub_type==i4_window_message_class::GOT_MOUSE_FOCUS)
      {
        CAST_PTR(mev, i4_window_got_mouse_focus_class, ev);

        mouse_x=mev->x;
        mouse_y=mev->y;

        i4_window_change_cursor_class c(this,cursor);
        i4_kernel.send_event(parent,&c); 
      } else if (wev->sub_type==i4_window_message_class::LOST_MOUSE_FOCUS)
      {
        i4_window_change_cursor_class c(this,0);   // unload the cursor
        i4_kernel.send_event(parent,&c); 
      }
    }     
  }
}

void i4_window_class::set_cursor(i4_cursor_class *Cursor)
{ 
  if (cursor)
    delete cursor;
  
  if (Cursor)
    cursor=Cursor->copy();
  else
    cursor=0;

  if (parent)
  {
    i4_window_change_cursor_class c(this,cursor,i4_F);
    i4_kernel.send_event(parent,&c);
  }
}

i4_window_class::~i4_window_class()
{  
  if (cursor)
    delete cursor;
  
  if (parent)
    parent->remove_child(this);
}


void i4_window_class::resize(w16 new_width, w16 new_height)
{
  if (parent)
  {
    i4_window_notify_resize_class resize(this,new_width,new_height);
    i4_kernel.send_event(parent,&resize);
  }
  private_resize(new_width,new_height);
}

void i4_parent_window_class::resize(w16 new_width, w16 new_height)
{
  i4_window_class::resize(new_width, new_height);

  i4_window_notify_resize_class resize(this,new_width,new_height);

  for (win_iter c=children.begin(); c!=children.end(); ++c)   
    send_event_to_child(&*c,&resize);
}


void i4_window_class::private_resize(w16 new_width, w16 new_height)
{
  i4_rect_list_class parent_dirty;
  if (width() && height())
    parent_dirty.add_area(0,0,width()-1, height()-1);

  w=new_width;
  h=new_height;
  parent_dirty.remove_area(0,0,width()-1,height()-1);

  if (width() && height())
    note_undrawn(0,0,width()-1,height()-1);

    
  if (parent)
  {
    for (i4_rect_list_class::area_iter a=parent_dirty.list.begin();
         a!=parent_dirty.list.end(); ++a)
    {
      parent->note_undrawn(a->x1 + x() - parent->x(),
                           a->y1 + y() - parent->y(),
                           a->x2 + x() - parent->x(),
                           a->y2 + y() - parent->y());
                           
    }
  }

  request_redraw();
}


void i4_window_class::reparent(i4_image_class *draw_area, i4_parent_window_class *Parent)
{
  parent=Parent;
  if (parent)
  {
    mouse_x=parent->last_mouse_x()+parent->x()-x();
    mouse_y=parent->last_mouse_y()+parent->y()-y();
  }
  else
  {
    mouse_x=-10000;
    mouse_y=-10000;
  }

  if (local_image)
    local_image=0;

  global_image=draw_area;
  if (global_image)
  {
    local_image=global_image;
    
    if (width() & height())
      note_undrawn(0,0,width()-1,height()-1);

    request_redraw();
  }
}

void i4_window_class::private_move(i4_coord x_offset, i4_coord y_offset)
{
  global_x+=x_offset;
  global_y+=y_offset; 

  if (width() && height())
    note_undrawn(0,0,width()-1,height()-1);


  request_redraw();
}

void i4_window_class::move(i4_coord x_offset, i4_coord y_offset,  i4_bool draw_under)
{
  private_move(x_offset,y_offset);
  if (parent)
  {
    i4_window_notify_move_class move(this,x_offset,y_offset,draw_under);
    i4_kernel.send_event(parent,&move);
  }
}

void i4_window_class::draw(i4_draw_context_class &context)
{
  redraw_flag=i4_F;
}

void i4_window_class::request_redraw(i4_bool for_a_child)
{
  redraw_flag=i4_T; 
    
  if (parent) 
  {
    if (transparent() && width() && height())
    {
      int x1=x()-parent->x(), y1=y()-parent->y();
      int x2=x1+width()-1, y2=y1+height()-1;

      parent->note_undrawn(x1,y1,x2,y2, i4_F);
    }

    parent->request_redraw(i4_T);
  }
}

void i4_window_class::note_undrawn(i4_coord x1, i4_coord y1, i4_coord x2, i4_coord y2,
                                   i4_bool propogate_to_children)
{
  request_redraw(i4_F);
}



void i4_parent_window_class::parent_draw(i4_draw_context_class &context)
{
  redraw_flag=i4_F;
}

// draw should redraw supplied area of self
void i4_parent_window_class::draw(i4_draw_context_class &context)

{  
  if (!undrawn_area.empty() || redraw_flag)
  {   
    pf_parent_draw_prepare.start();

    redraw_flag=i4_F;
    // copy the clip list, which should already be in our coordinates
    i4_rect_list_class child_clip(&context.clip,0,0);  
    
    // remove the area of other children above us    
    win_iter d=children.begin();
    for (;d!=children.end();++d)
    {
      if (!d->transparent())
        child_clip.remove_area(d->x()-x(),
                               d->y()-y(),
                               d->x()+d->width()-1-x(),
                               d->y()+d->height()-1-y());
    }

    child_clip.swap(&context.clip);

    pf_parent_draw_prepare.stop();

    parent_draw(context);

    pf_parent_draw_prepare.start();
    undrawn_area.delete_list();

    child_clip.swap(&context.clip);
    pf_parent_draw_prepare.stop();

  }

  if (child_need_redraw)
  {
    child_need_redraw=i4_F;
    win_iter c=children.begin();
    for (;c!=children.end();++c)   
    {
      if (c->need_redraw())
      {
        pf_child_draw_prepare.start();

	i4_coord x1=c->x()-x(),
          y1=c->y()-y(),
          x2=c->x()+c->width()-1-x(),
          y2=c->y()+c->height()-1-y();

        // copy the clip list, and move it to local coordinates
        i4_rect_list_class child_clip(&context.clip,
                                      -(c->x()-x()),
                                      -(c->y()-y()));

        // intersect the clip with what our area is supposed to be
        child_clip.intersect_area(0,0,
                                  c->width()-1,
                                  c->height()-1);   

        // save the old context xoff and yoff
        sw16 old_xoff=context.xoff,old_yoff=context.yoff;   
          
        // move the context offset to the child's x,y
        context.xoff+=(c->x()-x());                         
        context.yoff+=(c->y()-y());

	  // remove the area of other children above us
        i4_window_class *d=c->next;
        for (;d;d=d->next)
          child_clip.remove_area(d->x()-c->x(), 
                                 d->y()-c->y(),
                                 d->x()+d->width()-1-c->x(),
                                 d->y()+d->height()-1-c->y()); 

        c->call_stack_counter++;  // make sure window doesn't get deleted during it's draw

        // the child is covered completely by other windows
        // tell it this in case it depends on draw()
        if (child_clip.empty())
          c->forget_redraw();   
        else
        {
          child_clip.swap(&context.clip);
          
          child_rerequested_redraw = i4_F;
         
          pf_child_draw_prepare.stop();

          c->draw(context);         // the child is ready to draw

          pf_child_draw_prepare.start();
          if (!child_rerequested_redraw)          
            c->i4_window_class::forget_redraw();

          child_clip.swap(&context.clip);   // restore the old clip list
        }
        
        context.xoff=old_xoff;    // restore the context's x & y offsets
        context.yoff=old_yoff;

        c->call_stack_counter--;
        pf_child_draw_prepare.stop();

      }      
    }
  } 

}
    
#if ( __linux || __sgi)
//extern void db_show();
#else
//void db_show() { ; }
#endif

void i4_window_class::show_context(i4_draw_context_class &context)
{
  i4_draw_context_class nc(0,0,local_image->width(),local_image->height());
  local_image->clear(0xffff,nc);
  i4_rect_list_class::area_iter c=context.clip.list.begin();
  for (; c!=context.clip.list.end(); ++c)
    local_image->rectangle(c->x1,c->y1,c->x2,c->y2,0,nc);

  //  db_show();
}


i4_window_class::i4_window_class(w16 w, w16 h) : w(w),h(h)
{
  mouse_x=-10000;
  mouse_y=-10000;

  global_x=0; 
  global_y=0;
  local_image=0;
  global_image=0;
  parent=0;
  cursor=0;
}


i4_parent_window_class::i4_parent_window_class(w16 w, w16 h) : i4_window_class(w,h)
{
  have_mouse_focus=i4_F;
  mouse_focus_grabbed=i4_F;
  key_focus=children.end();
  mouse_focus=children.end();
  drag_drop_focus=children.end();
}

i4_parent_window_class::win_iter i4_parent_window_class::find_window(i4_coord global_mouse_x, 
                                                                     i4_coord global_mouse_y)
{
  win_iter c=children.begin(),
    find=children.end();

  for (;c!=children.end();++c)
  {
    if (global_mouse_x>=c->x() &&  
        global_mouse_y>=c->y() && 
        global_mouse_xx()+c->width() && 
        global_mouse_yy()+c->height())
      find=c;
  }
  return find;
}

void i4_parent_window_class::drag_drop_move(i4_event *ev)
{
  CAST_PTR(move, i4_window_drag_drop_move_class, ev);
  i4_coord real_mx=move->x+x(),
    real_my=move->y+y();

  win_iter find=find_window(real_mx, real_my);
  if (find != drag_drop_focus)
  {
    if (drag_drop_focus != children.end())
    {
      i4_window_lost_drag_drop_focus_class lost(this);
      send_event_to_child(&*drag_drop_focus, &lost);
    }

    drag_drop_focus = find;
    if (find != children.end())
    {
      i4_window_class *prev_from=move->from_window;
      move->from_window=this;

      send_event_to_child(&*find, move);

      move->from_window=prev_from;
    }
  }

  if (drag_drop_focus != children.end())
    send_event_to_child(&*drag_drop_focus,ev);
}


i4_bool i4_parent_window_class::find_new_mouse_focus()
{
  if (!mouse_focus_grabbed)  // see if we need to change the mouse focus
  {
    win_iter new_mouse_focus=find_window(mouse_x + x(), mouse_y + y());

    if (new_mouse_focus!=mouse_focus)
    {
      change_mouse_focus(&*new_mouse_focus);
      return i4_T;
    }

    else return i4_F;
  }
  else return i4_F;
}

void i4_parent_window_class::mouse_move(i4_event *ev)
{
  CAST_PTR(move, i4_mouse_move_event_class, ev);
  mouse_x=move->x;
  mouse_y=move->y;

  find_new_mouse_focus();

  if (mouse_focus!=children.end())
    send_event_to_child(&*mouse_focus,ev);
}

void i4_parent_window_class::send_event_to_child(i4_window_class *w, i4_event *ev)
{
  sw32 xo=(sw32)x()-(sw32)w->x(), yo=(sw32)y()-(sw32)w->y();

  ev->move(xo, yo);
  i4_kernel.send_event(w, ev);
  ev->move(-xo, -yo);
}

// passes events to mouse_focus or key_focus depending on the event type
void i4_parent_window_class::receive_event(i4_event *ev)
{
  switch (ev->type())
  {
    case i4_event::IDLE_MESSAGE :     // pass idle message to current mouse focus
    {
      if (mouse_focus!=children.end())
        send_event_to_child(&*mouse_focus,ev);
    } break;

    case i4_event::MOUSE_MOVE :
    {
      mouse_move(ev);
    } break;

    case i4_event::MOUSE_BUTTON_DOWN :
    {
      if (mouse_focus!=children.end())
        send_event_to_child(&*mouse_focus,ev);
    } break;

    case i4_event::MOUSE_BUTTON_UP :
    {
      if (mouse_focus!=children.end())
        send_event_to_child(&*mouse_focus,ev);
    } break;

    case i4_event::KEY_PRESS :
    case i4_event::KEY_RELEASE :
    {
      if (key_focus!=children.end())
        send_event_to_child(&*key_focus,ev);
      else if (mouse_focus!=children.end())
        send_event_to_child(&*mouse_focus,ev);
    } break;


    case i4_event::WINDOW_MESSAGE :
    {
      CAST_PTR(mess,i4_window_message_class,ev);
      switch (mess->sub_type)
      {	
        case i4_window_message_class::GOT_DROP :
        {
          if (drag_drop_focus != children.end())
          {
            send_event_to_child(&*drag_drop_focus, ev);
            drag_drop_focus=children.end();
          }
          else
          {
            CAST_PTR(dev, i4_window_got_drop_class, ev);

            i4_parent_window_class::win_iter w=find_window(dev->drag_info.x+x(),
                                                           dev->drag_info.y+y());
            if (w!=children.end())
              send_event_to_child(&*w, ev);
          }
        } break;

        case i4_window_message_class::REQUEST_DRAG_DROP_START :
        case i4_window_message_class::REQUEST_DRAG_DROP_END :
        {
          if (parent)
            parent->receive_event(ev);
        } break;
        case i4_window_message_class::DRAG_DROP_MOVE :
        {
          drag_drop_move(ev);
        } break;

        case i4_window_message_class::CHANGE_CURSOR :
        {
          CAST_PTR(cc,i4_window_change_cursor_class,ev);
          if (parent && (!cc->only_if_active || cc->from()==&*mouse_focus))
          {
            i4_window_change_cursor_class scope(this,cc->cursor,cc->only_if_active);
            parent->receive_event(&scope);
          }

        } break;
	case i4_window_message_class::REQUEST_KEY_GRAB :
	{
          CAST_PTR(grab,i4_window_request_key_grab_class,ev);
          grab->return_result=i4_T;

          if (parent)
          {
            i4_window_request_key_grab_class ask_parent(this);
            i4_kernel.send_event(parent,&ask_parent);
            if (ask_parent.return_result==i4_F)
              grab->return_result=i4_F;
          }

          if (grab->return_result)
          {
            win_iter c=children.begin();
            for (;c!=children.end() && c!=mess->from();++c);
            if (c!=children.end())
            {
              if (c != key_focus)
                change_key_focus(&*c);
            }
            else
              i4_warning("got key grab from unknown child");
          }
	} break;
	case i4_window_message_class::REQUEST_DELETE :
	{
	  remove_child(mess->from());
	  delete mess->from();
	} break;
	
	case i4_window_message_class::REQUEST_NEXT_KEY_FOCUS : next_key_focus(); break;
	case i4_window_message_class::REQUEST_LEFT_KEY_FOCUS : left_key_focus(); break;
	case i4_window_message_class::REQUEST_RIGHT_KEY_FOCUS : right_key_focus(); break;
	case i4_window_message_class::REQUEST_UP_KEY_FOCUS : up_key_focus(); break;
	case i4_window_message_class::REQUEST_DOWN_KEY_FOCUS : down_key_focus(); break;
	case i4_window_message_class::REQUEST_MOUSE_GRAB :
	{
	  CAST_PTR(grab,i4_window_request_mouse_grab_class,ev);

          grab->return_result=i4_T;    // defulat return is false

	  if (!mouse_focus_grabbed)  // see if we need to change the mouse focus	  
	  {
	    if (parent)
	    {
	      i4_window_request_mouse_grab_class ask_parent(this);
	      i4_kernel.send_event(parent,&ask_parent);
	      if (ask_parent.return_result==i4_F)
	        grab->return_result=i4_F;
              else grab->return_result=i4_T;
	    }

	    if (grab->return_result)
            {
              mouse_focus_grabbed=i4_T;
  
              if (&(*mouse_focus) != grab->from())
                change_mouse_focus(grab->from());
            }

	  } else 
	    grab->return_result=i4_F;
	} break;

	case i4_window_message_class::REQUEST_MOUSE_UNGRAB :
	{
	  if (mouse_focus_grabbed)
          {
            mouse_focus_grabbed=i4_F;
            if (parent)
              i4_kernel.send_event(parent,ev);	  

            find_new_mouse_focus();
          }
          else i4_warning("mouse not grabbed");

	} break;


	case i4_window_message_class::NOTIFY_RESIZE :
	{
	  CAST_PTR(resize,i4_window_notify_resize_class,ev);
          i4_window_class *cf=resize->from();

          if (cf!=parent)    // if this is from our parent, ignore it
          {
            i4_rect_list_class dirty;
            if (cf->width() && cf->height())
              dirty.add_area   (cf->x(),cf->y(),cf->x()+cf->width()-1,cf->y()+cf->height()-1);

            if (resize->new_width && resize->new_height)
              dirty.remove_area(cf->x(),cf->y(),
                                cf->x()+resize->new_width-1,
                                cf->y()+resize->new_height-1);

            if (width() && height())
              dirty.intersect_area(x(),y(),x()+width()-1,y()+height()-1);

            if (resize->draw_covered)
            {
              // check to see if any child were under this window 
              // will need to be redraw because of this
              win_iter c=children.begin();
              for (;c!=children.end() && c!=cf;++c)
              {                 
                if (c->width() && c->height() &&
                    !dirty.clipped_away(c->x(),c->y(),c->x()+c->width()-1,c->y()+height()-1))
                {
                  i4_rect_list_class::area_iter a=dirty.list.begin();
                  for (;a!=dirty.list.end();++a)
                  {
                    if (c->width() && c->height())
                    {
                      i4_coord x1=a->x1-c->x(),y1=a->y1-c->y(),x2=a->x2-c->x(),y2=a->y2-c->y();

                      if (x1<0) x1=0;
                      if (y1<0) y1=0;
                      if (x2>=c->width()) x2=c->width()-1;
                      if (y2>=c->height()) y2=c->height()-1;
                      if (x1<=x2 && y1<=y2)
                        c->note_undrawn(x1,y1,x2,y2);
                    }
                  }
                }
              }

              // add this dirty area to the parent's undrawn_area list, 
              // so the parent knows what part of itself it needs to redraw
              i4_rect_list_class::area_iter a=dirty.list.begin();
              for (;a!=dirty.list.end();++a)
                undrawn_area.add_area(a->x1-x(),    // make sure we add in parent-local coordinates
                                      a->y1-y(),
                                      a->x2-x(),
                                      a->y2-y());
            }
          }
          else 
            request_redraw(i4_F);
	} break;

	case i4_window_message_class::NOTIFY_MOVE :
	{
	  CAST_PTR(move_event,i4_window_notify_move_class,ev);
	  i4_window_class *child=move_event->from();
	  i4_coord old_x=child->x()-move_event->x_offset,old_y=child->y()-move_event->y_offset;

	  i4_coord new_x=child->x(),new_y=child->y();

	  if (move_event->draw_covered)
	  {
	    i4_rect_list_class dirty;

            if (child->width() && child->height())
              dirty.add_area(old_x,old_y,old_x+child->width()-1,old_y+child->height()-1);

            if (width() && height())
              dirty.intersect_area(x(),y(),x()+width()-1,y()+height()-1);

            if (child->width() && child->height())
              dirty.remove_area(new_x,new_y,new_x+child->width()-1,new_y+child->height()-1);
	    

      // check to see if any child were under this window will need to be redraw because of this
	    win_iter c=children.begin();
	    for (;c!=children.end() && c!=child;++c)
            {
              if (c->width() && c->height() &&
                  !dirty.clipped_away(c->x(),c->y(),c->x()+c->width()-1,c->y()+height()-1))
              {
                i4_rect_list_class::area_iter a=dirty.list.begin();
                for (;a!=dirty.list.end();++a)
                {
                  if (c->width() && c->height())
                  {
                    i4_coord x1=a->x1-c->x(),y1=a->y1-c->y(),x2=a->x2-c->x(),y2=a->y2-c->y();

                    if (x1<0) x1=0;
                    if (y1<0) y1=0;
                    if (x2>=c->width()) x2=c->width()-1;
                    if (y2>=c->height()) y2=c->height()-1;
                    if (x1<=x2 && y1<=y2)
                      c->note_undrawn(x1,y1,x2,y2);
                  }
                }
              }
            }

	    // add this dirty area to the parent's dirty_area list, 
            // so the parent knows what part of itself it needs to redraw
	    i4_rect_list_class::area_iter a=dirty.list.begin();
	    for (;a!=dirty.list.end();++a)
	      undrawn_area.add_area(a->x1-x(),   // add in local coordinates
                                    a->y1-y(),
                                    a->x2-x(),
                                    a->y2-y());
	  }

	} break;

        case i4_window_message_class::GOT_MOUSE_FOCUS :
        {
          have_mouse_focus=i4_T;

          CAST_PTR(mev, i4_window_got_mouse_focus_class, ev);
          mouse_x=mev->x;
          mouse_y=mev->y;

          i4_window_change_cursor_class c(this,cursor);
          i4_kernel.send_event(parent,&c); 

          find_new_mouse_focus();
        } break;


        case i4_window_message_class::LOST_DROP_FOCUS :
        {
          if (drag_drop_focus!=children.end())
          {
            send_event_to_child(&*drag_drop_focus,ev);
            drag_drop_focus=children.end();
          }
          i4_window_class::receive_event(ev);
        } break;

        case i4_window_message_class::LOST_MOUSE_FOCUS :
        {
          if (mouse_focus!=children.end())
          {
            send_event_to_child(&*mouse_focus,ev);
            mouse_focus=children.end();
          }
          i4_window_class::receive_event(ev);
          have_mouse_focus=i4_F;

        } break;
        case i4_window_message_class::LOST_KEYBOARD_FOCUS :
        {
          change_key_focus(0);
        } break;
        default :
          i4_window_class::receive_event(ev);
          
      }
    } break;
    default :
      i4_window_class::receive_event(ev);
          
  }
}

void i4_parent_window_class::change_key_focus(i4_window_class *new_focus)
{
  if (key_focus!=children.end())
  {
    i4_window_message_class lost(i4_window_message_class::LOST_KEYBOARD_FOCUS,this);
    send_event_to_child(&*key_focus,&lost);    
  }

  key_focus=new_focus;

  if (new_focus)
  {
    i4_window_message_class got(i4_window_message_class::GOT_KEYBOARD_FOCUS,this);
    send_event_to_child(&*key_focus,&got);  
  }
}


void i4_parent_window_class::change_mouse_focus(i4_window_class *new_focus)
{
  if (mouse_focus!=children.end())
  {
    i4_window_lost_mouse_focus_class lost(this, new_focus);
    send_event_to_child(&*mouse_focus,&lost);    
  }


  mouse_focus=new_focus;

  if (mouse_focus!=children.end())
  {
    i4_window_got_mouse_focus_class got(this, mouse_x, mouse_y);
    send_event_to_child(&*mouse_focus,&got);  
  }
}


void i4_parent_window_class::next_key_focus()
{
  if (key_focus!=children.end())
    change_key_focus(key_focus->next);
}


void i4_parent_window_class::left_key_focus()
{
  if (key_focus!=children.end())
  {
    win_iter c=children.begin(),closest=children.end();
    w32 closest_distance=0xffffffff;
    i4_coord cx1=key_focus->x()+key_focus->width()/2,
      cy1=key_focus->y()+key_focus->height()/2;


    for (;c!=children.end();++c)
    {
      if (c->x()x())
      {
	i4_coord cx2=c->x()+c->width()/2,cy2=c->y()+c->height()/2;

	w32 dist=(cx2-cx1)*(cx2-cx1)+(cy2-cy1)*(cy2-cy1);
	if (distx()+key_focus->width()/2,
      cy1=key_focus->y()+key_focus->height()/2;


    for (;c!=children.end();++c)
    {
      if (c->x()>cx1)
      {
	i4_coord cx2=c->x()+c->width()/2,
          cy2=c->y()+c->height()/2;       
	w32 dist=(cx2-cx1)*(cx2-cx1)+(cy2-cy1)*(cy2-cy1);
	if (distx()+key_focus->width()/2,
      cy1=key_focus->y()+key_focus->height()/2;


    for (;c!=children.end();++c)
    {
      if (c->y()y())
      {
	i4_coord cx2=c->x()+c->width()/2,
          cy2=c->y()+c->height()/2;       
	w32 dist=(cx2-cx1)*(cx2-cx1)+(cy2-cy1)*(cy2-cy1);
	if (distx()+key_focus->width()/2,
      cy1=key_focus->y()+key_focus->height()/2;

    for (;c!=children.end();++c)
    {
      if (c->y()>key_focus->y())
      {
	i4_coord cx2=c->x()+c->width()/2,
          cy2=c->y()+c->height()/2;       
	w32 dist=(cx2-cx1)*(cx2-cx1)+(cy2-cy1)*(cy2-cy1);
	if (distreparent(draw_area,this);

  i4_window_class::reparent(draw_area,_parent);

  if (have_mouse_focus)
    find_new_mouse_focus();
}

void i4_parent_window_class::add_child(i4_coord x_, i4_coord y_, i4_window_class *child)
{
  children.insert_end(*child);
  child->reparent(global_image,this);
  child->private_move(x()+x_-child->x(),y()+y_-child->y());

  if (have_mouse_focus)
    find_new_mouse_focus();
}

void i4_parent_window_class::add_child_front(i4_coord x_, i4_coord y_, i4_window_class *child)
{
  children.insert(*child);
  child->reparent(global_image,this);
  child->private_move(x()+x_-child->x(),y()+y_-child->y());

  if (have_mouse_focus)
    find_new_mouse_focus();
}


void i4_parent_window_class::forget_redraw()
{
  undrawn_area.delete_list();  
  for (win_iter c=children.begin();c!=children.end();++c)
    c->forget_redraw();
}

void i4_parent_window_class::transfer_children(i4_parent_window_class *other_parent, 
                                               i4_coord x_offset, i4_coord y_offset)
{ 
  while (children.begin() != children.end())
  {
    i4_window_class *c=&*children.begin();
    i4_coord xn=c->x()-x();
    i4_coord yn=c->y()-y();

    remove_child(c);
    other_parent->add_child(xn+x_offset,
                            yn+y_offset,c);
  }

  if (have_mouse_focus)
    find_new_mouse_focus();

  if (other_parent->has_mouse_focus())
    other_parent->find_new_mouse_focus();
}

void i4_parent_window_class::remove_child(i4_window_class *child)
{
  if (&*key_focus==child)
    change_key_focus(0); 

  if (&*mouse_focus==child)
  {
    if (mouse_focus_grabbed)
    {
      if (parent)
      {
        i4_window_request_mouse_ungrab_class ungrab(this);
        i4_kernel.send_event(parent, &ungrab);
      }

      mouse_focus_grabbed=i4_F;
    }
  }

  if (child==&*children.begin())
    children.erase();
  else
  {
    win_iter f=children.begin(),last=children.end();
    for (;f!=children.end() && f!=&*child;)
    {
      last=f;
      ++f;
    }
    if (f!=children.end())
      children.erase_after(last);
    else
      i4_error("child not found");
  }

  if (child->width() && child->height())
  {
    note_undrawn(child->x()-x(),
                 child->y()-y(),
                 child->x()+child->width()-1-x(),
                 child->y()+child->height()-1-y());
  }

  if (&*mouse_focus==child)
  {
    i4_window_lost_mouse_focus_class lost(this, 0);
    send_event_to_child(child, &lost);    
    mouse_focus=children.end();
  }


  child->reparent(0,0);  // tell the child it doesn't have a parent or a draw_area anymore

  if (have_mouse_focus)
    find_new_mouse_focus();

}

void i4_parent_window_class::request_redraw(i4_bool for_a_child)
{
  if (for_a_child)
  {
    child_need_redraw=i4_T;
    child_rerequested_redraw=i4_T;
  }
  else
    redraw_flag=i4_T;
  if (parent)
    parent->request_redraw(i4_T);
}

void i4_parent_window_class::note_undrawn(i4_coord x1, i4_coord y1, 
                                          i4_coord x2, i4_coord y2,
                                          i4_bool propogate_to_children)
{

  undrawn_area.add_area(x1,y1,x2,y2);
  request_redraw();
   
  win_iter c=children.begin();


  x1+=x();  y1+=y();     // transform to global coordinates
  x2+=x();  y2+=y();
  
  if (propogate_to_children)
  {
    for (;c!=children.end();++c)
    {
      if (c->width() && c->height())
      {
        if (!(c->x()>x2 || 
              c->y()>y2 || 
              c->x()+c->width()-1  < x1 || 
              c->y()+c->height()-1 < y1))
        {      
          i4_coord ax1=x1-c->x(),
            ay1=y1-c->y(),
            ax2=x2-c->x(),
            ay2=y2-c->y();
          if (ax1<0) ax1=0;
          if (ay1<0) ay1=0;
          if (ax2>=c->width()) ax2=c->width()-1;
          if (ay2>=c->height()) ay2=c->height()-1;
          if (ax1<=ax2 && ay1<=ay2)
            c->note_undrawn(ax1,ay1,ax2,ay2);
        }
      }
    }
  }
}



void i4_parent_window_class::private_move(i4_coord x_offset, i4_coord y_offset)
{
  i4_window_class::private_move(x_offset,y_offset);
  win_iter c=children.begin();
  for (;c!=children.end();++c)
  {
    c->private_move(x_offset,y_offset);  
  }
  // use private move so child doesn't tell us it moved (we know!)
}


void i4_parent_window_class::redraw_area(i4_coord x1, i4_coord y1, i4_coord x2, i4_coord y2)
{
  if (!(x2<0 || y2<0 || x1>=width() || y1>=height()))
    undrawn_area.add_area(x1,y1,x2,y2);
}

// arranges child windows from left to right then down
void i4_parent_window_class::arrange_right_down()  
{
  i4_coord x1=x(),y1=y(),mh=0;
  win_iter c=children.begin();
  for (;c!=children.end();++c)
  {

    if (c->width()+x1>x()+width())
    {
      y1+=mh;
      x1=x();
      mh=0;
    }

    c->move(x1-c->x(),y1-c->y());

    if (c->height()>mh)
      mh=c->height();

    x1+=c->width();


  }
}

void i4_parent_window_class::resize_to_fit_children()
{
  i4_coord x2=0,y2=0;
  win_iter c=children.begin();
  for (;c!=children.end();++c)
  {
    if (c->x()-x()+c->width()>x2)    
      x2=c->x()-x()+c->width();
    if (c->y()-y()+c->height()>y2)
      y2=c->y()-y()+c->height();
  }

  resize(x2,y2);
}

void i4_parent_window_class::arrange_down_right()                                 // arranges child windows from top to bottom then right
{
  i4_coord x1=x(),y1=y(),mw=0;
  win_iter c=children.begin();
  for (;c!=children.end();++c)
  {
    if (c->height()+y1>=height())
    {
      x1+=mw;
      y1=y();
      mw=0;
    }

    c->move(x1-c->x(),y1-c->y());
    y1+=c->height();

    if (c->width()>mw)
      mw=c->width();


  }
}


i4_parent_window_class::~i4_parent_window_class()
{
  while (children.begin() != children.end())
  {
    win_iter c=children.begin();
    children.erase();
    c->reparent(0,0);
    
    i4_kernel.delete_handler(&*c);
  }  
}

i4_window_class *i4_parent_window_class::get_nth_window(w32 win_num)
{
  win_iter i=children.begin();
  while (win_num && i!=children.end())
  {
    ++i;
    win_num--;
  }
  if (i==children.end())
    return 0;
  else return &*i;
}


#ifndef I4_RETAIL
void i4_window_class::debug_show()
{
  i4_warning("%s",name());
  if (parent)
    parent->debug_show();
}
#endif

i4_bool i4_parent_window_class::isa_child(i4_window_class *w)
{
  win_iter c=children.begin();
  for (;c!=children.end();++c)
    if (&*c==w)
      return i4_T;

  return i4_F;
}