
// ##########################################################################
// #                                                                        #
// #                               IconWindow                               #
// #                                                                        #
// #                            - Definitionen -                            #
// #                                                                        #
// ##########################################################################



#include "gui/IconWin.h"
#include "ui/cntroler.h"
#include "base/intset.h"
#include "gui/stdmsg.h"
#include "gui/keydefs.h"

#include "ui/color.h"


enum { ID_HSCROLLER = 20000, ID_VSCROLLER };
typedef CL_Binding0<IconWindow> IconWindowBind;


// ************
// Konstruktion
// ************


IconWindow :: IconWindow(UI_CompositeVObject *  parent,
								 const UI_Rectangle& shape,
								 UI_ViewID           id,
								 unsigned            dist
								)
				: UI_CompositeVObject (parent, shape, id)
{
  Distance = dist;
  HScroll  = new UI_HScrollBar (this, UI_Rectangle(), ID_HSCROLLER);
  VScroll  = new UI_VScrollBar (this, UI_Rectangle(), ID_VSCROLLER);
  HScroll -> MakeInvisible();
  VScroll -> MakeInvisible();
  HScroll -> LineAmount() = 20;
  VScroll -> LineAmount() = 20;
  IconWindowBind bdH(this, &IconWindow::scrollHorz);
  IconWindowBind bdV(this, &IconWindow::scrollVert);
  HScroll -> ClientSet().Add(bdH);
  VScroll -> ClientSet().Add(bdV);
  ScrlWdth = 20;
};



// ****************
// Attributszugriff
// ****************



unsigned& IconWindow :: distance()
{
  return Distance;
};


UI_HScrollBar& IconWindow :: hscroll ()
{
	 return *HScroll;
}


UI_VScrollBar& IconWindow :: vscroll ()
{
	 return *VScroll;
}



// *************
// Icon-Handling
// *************


Icon* IconWindow :: AddChild(const IconDescriptor& dsc)
{
  return new Icon(this,dsc);
};



bool IconWindow :: append(Icon* icn)
{
  if (icn && (Icons.LinearSearch(icn) == -1))
	{
	  UI_Vector v = newPos(icn->Shape().Width());
	  UI_Point pt(v.X(),v.Y());
	  icn->Shape().Origin(pt);
	  Icons.Add(icn);
	  checkScroll();
	  return TRUE;
	}
  else return FALSE;
};


bool IconWindow :: insertAt(Icon* icn, unsigned i)
{
  if (i >= Icons.Size())
		 return append(icn);
  else if (icn && (Icons.LinearSearch(icn) == -1))
		  {
			 Icons.Insert(icn,i);
			 arrange();
			 checkScroll();
			 return TRUE;
		  }
		 else return FALSE;
};


Icon* IconWindow :: remove(Icon* icn)
{
  long int idx = Icons.LinearSearch(icn);
  if (idx >= 0)
		 return removeAt((unsigned)idx);
  else return NULL;
};


Icon* IconWindow :: removeAt(unsigned i)
{
  Icon* res = NULL;
  if (i < Icons.Size())
	{
	  res = (Icon*)Icons[i];
	  Icons.Remove(i);
	  checkScroll();
	};
  return res;
};


void IconWindow :: destroyIcons ()
{
  for (unsigned i = Icons.Size(); i > 0; i--)
		_Application->Destroy((Icon*)(Icons[i-1]));
  return;
};


UI_Vector IconWindow :: newPos(unsigned w_new)
{
  long int x = Distance;   // x-coordinate of new icon
  long int y = Distance;   // y-coordinate of new icon
  if (Icons.Size() > 0)
	{
		Icon* Icn = (Icon*)Icons[Icons.Size()-1];      // rightmost icon
		if (Icn)
		 {
			x += Icn->Shape().Right();                  // Add to the right
			y  = Icn->Shape().Top();                    // at same y-coordinate.
			if ( x+w_new > _shape.Width()
				)                                        // If the new icon position
			 {                                          // conflicts with the
				x  = Distance;                           // Client's width, start
				y += Distance + Icn->Shape().Height();   // a new line.
			 };
		 };
	};
  return UI_Vector(x,y);
};


bool IconWindow :: arrange()
{
  long int W = _shape.Width();          // current width
  long int Y = 0;                       // highest y-Value
  long int x = Distance;                // x-coordinate
  long int y = Distance;                // y-coordinate
  long int w;                           // width of icon
  long int h;                           // height of icon
  Icon* Icn;                            // current icon
  for (unsigned i = 0; i < Icons.Size(); i++)
	 {
		Icn = (Icon*)Icons[i];
		if (Icn)
		 {
			w = Icn->Shape().Width();
			h = Icn->Shape().Height();
			if (w+x > W)                   // widht-limit exeeded ?
			 {
				x = Distance;               // then start a new line
				y = Y + Distance;
			 };
			Icn->Shape() = UI_Rectangle(x,y,w,h);
			x += w;
			y += h;
			if (y > Y)
				 Y = y;
		 };
	 };
  checkScroll();
  return TRUE;
};


Icon* IconWindow :: selectedIs() const
{
  Icon* res = NULL;
  Icon* icn;
  for (unsigned i = 0; i < Icons.Size() && !res; i++)
	 {
		icn = (Icon*)Icons[i];
		if (icn && icn->isSelected())
			 res = icn;
	 };
  return res;
};


void IconWindow :: selectedAre(CL_IntegerSet& set) const
{
  set.MakeEmpty();
  Icon* icn;
  for (unsigned i = 0; i < Icons.Size(); i++)
	 {
		icn = (Icon*)Icons[i];
		if (icn && icn->isSelected())
			 set.Add(i);
	 };
  return;
};


long int IconWindow :: selectedIdx() const
{
  long int res = -1;
  Icon* icn;
  for (unsigned i = 0; i < Icons.Size() && (res == -1); i++)
	 {
		icn = (Icon*)Icons[i];
		if (icn && icn->isSelected())
			 res = i;
	 };
  return res;
};


bool IconWindow :: selectAt(unsigned i)
{
  if ( (Icons.Size() > 0) && (i < Icons.Size()) && Icons[i] )
	{
	  ((Icon*)Icons[i])->Select();
	  return TRUE;
	}
  else return FALSE;
};


bool IconWindow :: unselectAt(unsigned i)
{
  if ( (Icons.Size() > 0) && (i < Icons.Size()) && Icons[i] )
	{
	  ((Icon*)Icons[i])->Unselect();
	  return TRUE;
	}
  else return FALSE;
};


bool IconWindow :: selectOnlyAt(unsigned i)
{
  unselectAll();
  return selectAt(i);
};


bool IconWindow :: selectNext()
{
  bool res = FALSE;
  if (Icons.Size() > 0)
	{
	  long int idx = selectedIdx()+1;
	  if (idx < Icons.Size())
		  res = selectOnlyAt(idx);
	};
  return res;
};


bool IconWindow :: selectPrev()
{
  bool res = FALSE;
  if (Icons.Size() > 0)
	{
	  long int idx = selectedIdx()-1;
	  if (idx < 0)
			idx = 0;
	  res = selectOnlyAt(idx);
	};
  return res;
};



bool IconWindow :: selectOnly(UI_ViewID id)
{
  bool res = FALSE;
  UI_VisualObject* obj = (*this)[id];
  if (obj)
	{
	  CL_String s = "Icon";
	  if (s == obj->ClassName())
		{
		  unselectAll();
		  ((Icon*)obj)->Select();
		  res = TRUE;
		};
	};
  return res;
};


bool IconWindow :: selectOnly(Icon* icn)
{
  if (Icons.LinearSearch(icn) != -1)
	{
	  unselectAll();
	  icn->Select();
	  return TRUE;
	}
  else return FALSE;
};


bool IconWindow :: selectAll()
{
  for (unsigned i = 0; i < Icons.Size(); i++)
		if (Icons[i])
			 ((Icon*)Icons[i])->Select();
  return TRUE;
};


bool IconWindow :: unselectAll()
{
  for (unsigned i = 0; i < Icons.Size(); i++)
		if (Icons[i])
			 ((Icon*)Icons[i])->Unselect();
  return TRUE;
};


bool IconWindow :: selectAll    (UI_Event&,UI_ViewID)
{
  return selectAll();
};


bool IconWindow :: unselectAll  (UI_Event&,UI_ViewID)
{
  return unselectAll();
};


bool IconWindow :: arrange      (UI_Event&,UI_ViewID)
{
  return arrange();
};



// ******************
// Scrollbar-Handling
// ******************


long int IconWindow :: right()
{
  long int res = 0;
  if (Icons.Size() > 0)
	  res = ((Icon*)(Icons[Icons.Size()-1]))->Shape().Right();
  return res;
};


long int IconWindow :: bottom()
{
  long int res = 0;
  if (Icons.Size() > 0)
	  res = ((Icon*)(Icons[Icons.Size()-1]))->Shape().Bottom();
  return res;
};


long int IconWindow :: left()
{
  long int res = 0;
  if (Icons.Size() > 0)
	  res = ((Icon*)(Icons[0]))->Shape().Left() - Distance;
  return res;
};


long int IconWindow :: top()
{
  long int res = 0;
  if (Icons.Size() > 0)
	  res = ((Icon*)(Icons[0]))->Shape().Top() - Distance;
  return res;
};


void IconWindow :: checkScroll()
{
  if (right() < _shape.Width())
		 HScroll->MakeInvisible();
  else {
			HScroll->MakeVisible();
			HScroll->Range() = CL_Interval(0,right()-left());
			CL_Interval& ivl = (CL_Interval&)(HScroll->Model());
			ivl = CL_Interval (-left(),_shape.Width());
		 };
  if (bottom() < _shape.Height())
		 VScroll->MakeInvisible();
  else {
			VScroll->MakeVisible();
			VScroll->Range() = CL_Interval(0,bottom()-top());
			CL_Interval& ivl = (CL_Interval&)(VScroll->Model());
			ivl = CL_Interval(-top(),_shape.Height());
		 };
  reshapeScroll();
  return;
};


bool IconWindow :: scrollHorz()
{
  if (HScroll->IsVisible())
	{
	  long int low = ((CL_Interval&)(HScroll->Model())).Low();
	  long int lft;
	  UI_Point ctr;
	  for (unsigned i = 0; i < Icons.Size(); i++)
		 {
			lft = ((Icon*)(Icons[i]))->Shape().Left();
			ctr = ((Icon*)(Icons[i]))->Shape().Center();
			ctr.AddToX(low - lft - Distance);
		 };
	};
  return TRUE;
};


bool IconWindow :: scrollVert()
{
  if (VScroll->IsVisible())
	{
	  long int hgh = ((CL_Interval&)(HScroll->Model())).High();
	  long int rgt;
	  UI_Point ctr;
	  for (unsigned i = 0; i < Icons.Size(); i++)
		 {
			rgt = ((Icon*)(Icons[i]))->Shape().Right();
			ctr = ((Icon*)(Icons[i]))->Shape().Center();
			ctr.AddToY(hgh - rgt - Distance);
		 };
	};
  return TRUE;
};



// **************
// Redefinitionen
// **************



void IconWindow :: AddChild(Icon* obj)
{
  UI_CompositeVObject :: AddChild(obj);
  append(obj);
  return;
};


void IconWindow :: AddChild(UI_VisualObject* obj)
{
  UI_CompositeVObject :: AddChild(obj);
  return;
};


UI_VisualObject* IconWindow :: RemoveChild(UI_VisualObject* obj)
{
  long int idx = Icons.LinearSearch(obj);
  if (idx != -1)
		Icons.Remove(idx);
  return UI_CompositeVObject :: RemoveChild(obj);
};


bool IconWindow ::KeyTyped (char c)
{
  bool res = UI_CompositeVObject::KeyTyped(c);
  switch (c)
  {
	 case KeyLeft : selectPrev(); break;
	 case KeyRight: selectNext(); break;
  };
  return res;
};


bool IconWindow ::ButtonDown (const UI_Point& pt, UI_MouseButton btn ,
										bool sh,bool ct)
{
  bool res = UI_CompositeVObject::ButtonDown(pt,btn,sh,ct);
  if (btn == UIM_Left && !ct)
	  unselectAll();
  return res;
};


bool IconWindow ::HandleChildEvent (const UI_Event& e)
{
  bool res = FALSE;
  if (
		  ( e.Origin() != this  )                // Check if it is a
	  && ( !e.CtrlKeyPressed() )                // relevant event for
	  && (  ( e.Type() == Event_Select )        // an icon...
		  || ( e.Type() == Event_LButtonPress )
		  || ( e.Type() == Event_MButtonPress )
		  || ( e.Type() == Event_RButtonPress )
		  )
	  )
	{
	  long int idx = Icons.LinearSearch(e.Origin());  //...and if the event
	  if (idx != -1)                                  // originated at an icon
		  res = selectOnlyAt(idx);  // in this case select icon uniquely
	};
 if (!res)
  {
	 if (e.Type() >= Event_Scroll && e.Type() <= Event_ScrollToPosition)
	  {
		 if (e.Origin() == HScroll)
		  {
			  scrollHorz();
			  res = TRUE;
		  }
		 else if (e.Origin() == VScroll)
				 {
					scrollVert();
					res = TRUE;
				 };
	  }
	 else	res= UI_CompositeVObject::HandleChildEvent(e);
  };
 return res;
};


bool IconWindow::_ShapeRectChanged ()
{
	 HScroll -> PageAmount() = _shape.Width () - 50;
	 VScroll -> PageAmount() = _shape.Height() - 50;
	 checkScroll();
	 return UI_CompositeVObject::_ShapeRectChanged();
};


bool IconWindow :: Reconfigure (const UI_Rectangle& r)
{
	 bool res = UI_CompositeVObject :: Reconfigure(r);
	 checkScroll();
	 return res;
};


void IconWindow :: reshapeScroll()
{
	 bool hvis = HScroll->IsVisible();
	 bool vvis = VScroll->IsVisible();
	 if (hvis || vvis)
	  {
		 UI_RectangleStruct shpH = { 0,
											  _shape.Height() - ScrlWdth,
											  _shape.Width(),
											  ScrlWdth
											};
		 UI_RectangleStruct shpV = { _shape.Width()  - ScrlWdth,
											  0,
											  ScrlWdth,
											  _shape.Height()
											};
		 if (hvis && vvis)
			  shpH.w -= ScrlWdth;
		 if (hvis)
			  HScroll->Shape() = shpH;
		 if (vvis)
			  VScroll->Shape() = shpV;
	  };
	return;
};
