//
// "$Id: Fl_Tile.cxx 9637 2012-07-24 04:37:22Z matt $"
//
// Tile widget for the Fast Light Tool Kit (FLTK).
//
// Copyright 1998-2010 by Bill Spitzak and others.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file.  If this
// file is missing or damaged, see the license at:
//
//     http://www.fltk.org/COPYING.php
//
// Please report all bugs and problems on the following page:
//
//     http://www.fltk.org/str.php
//


// Group of 2 or 4 "tiles" that can be resized by dragging border
// The size of the first child determines where the resize border is.
// The resizebox is used to limit where the border can be dragged to.

#include <FL/Fl.H>
#include <FL/Fl_Tile.H>
#include <FL/Fl_Window.H>
#include <stdlib.h>

// Drag the edges that were initially at oldx,oldy to newx,newy:
// pass zero as oldx or oldy to disable drag in that direction:
/** 
  Drag the intersection at from_x,from_y to to_x,to_y.
  This redraws all the necessary children.
*/
void Fl_Tile::position(int oix, int oiy, int newx, int newy) {
  Fl_Widget*const* a = array();
  st_widget_sizes *p = sizes();
  p += 2; // skip group & resizable's saved size
  for (int i=children(); i--; ++p) {
    Fl_Widget* o = *a++;
    if (o == resizable()) continue;
    int X = o->x();
    int R = X+o->w();
    if (oix) {
      int t = p->x;
      if (t == oix || (t>oix && X<newx) || (t<oix && X>newx)) X = newx;
      t = p->w;
      if (t == oix || (t>oix && R<newx) || (t<oix && R>newx)) R = newx;
    }
    int Y = o->y();
    int B = Y+o->h();
    if (oiy) {
      int t = p->y;
      if (t == oiy || (t>oiy && Y<newy) || (t<oiy && Y>newy)) Y = newy;
      t = p->h;
      if (t == oiy || (t>oiy && B<newy) || (t<oiy && B>newy)) B = newy;
    }
    o->damage_resize(X,Y,R-X,B-Y);
  }
}

// move the lower-right corner (sort of):
void Fl_Tile::resize(int X,int Y,int W,int H) {
  //Fl_Group::resize(X, Y, W, H);
  //return;
  // remember how much to move the child widgets:
  int dx = X-x();
  int dy = Y-y();
  int dw = W-w();
  int dh = H-h();
  st_widget_sizes *p = sizes();
  // resize this (skip the Fl_Group resize):
  Fl_Widget::resize(X,Y,W,H);
  // find bottom-right of resiable:
  int OR = (p+1)->w;
  int NR = X+W-(p->w-OR);
  int OB = (p+1)->h;
  int NB = Y+H-(p->h-OB);
  // move everything to be on correct side of new resizable:
  Fl_Widget*const* a = array();
  p += 2;
  for (int i=children(); i--; ++p) {
    Fl_Widget* o = *a++;
    int xx = o->x()+dx;
    int R = xx+o->w();
    if (p->x >= OR) xx += dw; else if (xx > NR) xx = NR;
    if (p->w >= OR) R += dw; else if (R > NR) R = NR;
    int yy = o->y()+dy;
    int B = yy+o->h();
    if (p->y >= OB) yy += dh; else if (yy > NB) yy = NB;
    if (p->h >= OB) B += dh; else if (B > NB) B = NB;
    o->resize(xx,yy,R-xx,B-yy);
    // do *not* call o->redraw() here! If you do, and the tile is inside a 
    // scroll, it'll set the damage areas wrong for all children!
  }
}

static void set_cursor(Fl_Tile*t, Fl_Cursor c) {
  static Fl_Cursor cursor;
  if (cursor == c || !t->window()) return;
  cursor = c;
#ifdef __sgi
  t->window()->cursor(c,FL_RED,FL_WHITE);
#else
  t->window()->cursor(c);
#endif
}

static Fl_Cursor cursors[4] = {
  FL_CURSOR_DEFAULT,
  FL_CURSOR_WE,
  FL_CURSOR_NS,
  FL_CURSOR_MOVE};

int Fl_Tile::handle(int event) {
  static int sdrag;
  static int sdx, sdy;
  static int sx, sy;
#define DRAGH 1
#define DRAGV 2
#define GRABAREA 4

  int mx = Fl::event_x();
  int my = Fl::event_y();

  switch (event) {

  case FL_MOVE:
  case FL_ENTER:
  case FL_PUSH:
    // don't potentially change the mouse cursor if inactive:
    if (!active()) break; // will cascade inherited handle()
    {
    int mindx = 100;
    int mindy = 100;
    int oldx = 0;
    int oldy = 0;
    Fl_Widget*const* a = array();
    st_widget_sizes *q = sizes();
    st_widget_sizes *p = q+2;
    for (int i=children(); i--; ++p) {
      Fl_Widget* o = *a++;
      if (o == resizable()) continue;
      if (p->w < q->w && o->y()<=my+GRABAREA && o->y()+o->h()>=my-GRABAREA) {
	int t = mx - (o->x()+o->w());
	if (abs(t) < mindx) {
	  sdx = t;
	  mindx = abs(t);
	  oldx = p->w;
	}
      }
      if (p->h < q->h && o->x()<=mx+GRABAREA && o->x()+o->w()>=mx-GRABAREA) {
	int t = my - (o->y()+o->h());
	if (abs(t) < mindy) {
	  sdy = t;
	  mindy = abs(t);
	  oldy = p->h;
	}
      }
    }
    sdrag = 0; sx = sy = 0;
    if (mindx <= GRABAREA) {sdrag = DRAGH; sx = oldx;}
    if (mindy <= GRABAREA) {sdrag |= DRAGV; sy = oldy;}
    set_cursor(this, cursors[sdrag]);
    if (sdrag) return 1;
    return Fl_Group::handle(event);
  }

  case FL_LEAVE:
    set_cursor(this, FL_CURSOR_DEFAULT);
    break;

  case FL_DRAG:
    // This is necessary if CONSOLIDATE_MOTION in Fl_x.cxx is turned off:
    // if (damage()) return 1; // don't fall behind
  case FL_RELEASE: {
    if (!sdrag) return 0; // should not happen
    Fl_Widget* r = resizable(); if (!r) r = this;
    int newx;
    if (sdrag&DRAGH) {
      newx = Fl::event_x()-sdx;
      if (newx < r->x()) newx = r->x();
      else if (newx > r->x()+r->w()) newx = r->x()+r->w();
    } else
      newx = sx;
    int newy;
    if (sdrag&DRAGV) {
      newy = Fl::event_y()-sdy;
      if (newy < r->y()) newy = r->y();
      else if (newy > r->y()+r->h()) newy = r->y()+r->h();
    } else
      newy = sy;
    position(sx,sy,newx,newy);
    if (event == FL_DRAG) set_changed();
    do_callback();
    return 1;}

  }

  return Fl_Group::handle(event);
}


Fl_Tile::Fl_Tile(int X,int Y,int W,int H,const char*l) 
: Fl_Group(X,Y,W,H,l) 
{
}


//
// End of "$Id: Fl_Tile.cxx 9637 2012-07-24 04:37:22Z matt $".
//
