/**************************************************************************

    This file is part of Xclass95, a Win95-looking GUI toolkit.
    Copyright (C) 1996, 1997 David Barth, Hector Peraza.

    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., 675 Mass Ave, Cambridge, MA 02139, USA.

**************************************************************************/

#include <xclass/utils.h>
#include <xclass/OXFrame.h>


//--------------------------------------------------------------------

OVerticalLayout::OVerticalLayout(class OXCompositeFrame *main) { 
  _main = main; 
  _list = _main->GetListAddr();
}

void OVerticalLayout::Layout() {
  SListFrameElt *ptr;
  OLayoutHints *layout;
  int nb_expand = 0;
  int top, bottom;
  unsigned long hints;
  unsigned int extra_space;
  int exp = 0;
  int exp_max = 0;
  int remain;
  int x = 0, y = 0;
  int bw = _main->GetBorderWidth();
  ODimension size, csize;
  ODimension msize = _main->GetSize();
  int pad_left, pad_top, pad_right, pad_bottom;

  if (*_list == NULL) return;

  bottom = msize.h - (top = bw);
  remain = msize.h - (bw << 1);

  for (ptr=*_list; ptr != NULL; ptr = ptr->next) {

    if (ptr->state & IS_VISIBLE) {
      layout = ptr->layout;
      size = ptr->frame->GetDefaultSize();
      size.h += layout->GetPadTop() + layout->GetPadBottom();
      hints = layout->GetLayoutHints();
      if ((hints & LHINTS_EXPAND_Y) || (hints & LHINTS_CENTER_Y)) {
	nb_expand++;
	exp += size.h;
	exp_max = min(exp_max, size.h);
      } else
	remain -= size.h;
    }
  }

  for (ptr=*_list; ptr != NULL; ptr=ptr->next) {

    if (ptr->state & IS_VISIBLE) {

      hints = (layout = ptr->layout)->GetLayoutHints();
      csize      = ptr->frame->GetDefaultSize();
      pad_left   = layout->GetPadLeft();
      pad_top    = layout->GetPadTop();
      pad_right  = layout->GetPadRight();
      pad_bottom = layout->GetPadBottom();

      if (hints & LHINTS_RIGHT)
	x = msize.w - bw - csize.w - pad_right;
      else if (hints & LHINTS_CENTER_X)
	x = (msize.w - (bw << 1) - csize.w) >> 1; 
      else // defaults to LHINTS_LEFT
	x = pad_left + bw;
      
      if (hints & LHINTS_EXPAND_X) {
	size.w = msize.w - (bw << 1) - pad_left - pad_right;
	x = pad_left + bw;
      } else {
	size.w = csize.w;
      }

      if (hints & LHINTS_EXPAND_Y) {
	if ((remain / nb_expand) >= exp_max)
	  size.h = (remain / nb_expand) - pad_top - pad_bottom;
	else
          size.h = csize.h + (remain - exp) / nb_expand;
      } else {
	size.h = csize.h;
	if (hints & LHINTS_CENTER_Y) {
	  if ((remain / nb_expand) >= exp_max)
	    extra_space = ((remain / nb_expand) - pad_top - pad_bottom - size.h) >> 1;
	  else
	    extra_space = ((remain - exp) / nb_expand) >> 1;
	  y += extra_space;
	  top += extra_space;
	}
      }

      if (hints & LHINTS_BOTTOM) {
	y = bottom - size.h - pad_bottom;
	bottom -= size.h + pad_top + pad_bottom;
      } else { // LHINTS_TOP by default
	y = top + pad_top;
	top += size.h + pad_top + pad_bottom;
      }

      if (hints & LHINTS_CENTER_Y)
	top += extra_space;

      ptr->frame->MoveResize(x, y, size.w, size.h);
    }
  }
}

ODimension OVerticalLayout::GetDefaultSize() const {
  SListFrameElt *ptr;
  ODimension size(0,0), msize = _main->GetSize(), csize;
  int options = _main->GetOptions();

  if ((options & FIXED_WIDTH) && (options & FIXED_HEIGHT))
    return msize;

  for (ptr=*_list; ptr != NULL; ptr=ptr->next) {
    if (ptr->state & IS_VISIBLE) {
      csize = ptr->frame->GetDefaultSize();
      size.w = max(size.w, csize.w + ptr->layout->GetPadLeft() +
                                     ptr->layout->GetPadRight());
      size.h += csize.h + ptr->layout->GetPadTop() +
                          ptr->layout->GetPadBottom();
    }
  }

  size.w += _main->GetBorderWidth() << 1;
  size.h += _main->GetBorderWidth() << 1;

  if (options & FIXED_WIDTH) size.w = msize.w;
  if (options & FIXED_HEIGHT) size.h = msize.h;

  return size;
}

void OHorizontalLayout::Layout() {
  SListFrameElt *ptr;
  OLayoutHints *layout;
  int nb_expand = 0;
  int left, right;
  unsigned long hints;
  unsigned int extra_space;
  int exp = 0;
  int exp_max = 0;
  int remain;
  int x = 0, y = 0;
  int bw = _main->GetBorderWidth();
  ODimension size, csize;
  ODimension msize = _main->GetSize();
  int pad_left, pad_top, pad_right, pad_bottom;

  if (*_list == NULL) return;

  right  = msize.w - (left = bw);
  remain = msize.w - (bw << 1);

  for (ptr=*_list; ptr != NULL; ptr = ptr->next) {

    if (ptr->state & IS_VISIBLE) {
      layout  = ptr->layout;
      size    = ptr->frame->GetDefaultSize();
      size.w += layout->GetPadLeft() + layout->GetPadRight();
      hints = layout->GetLayoutHints();
      if ((hints & LHINTS_EXPAND_X) || (hints & LHINTS_CENTER_X)) {
	nb_expand++;
	exp += size.w;
	exp_max = min(exp_max, size.w);
      } else
	remain -= size.w;
    }
  }

  for (ptr=*_list; ptr != NULL; ptr=ptr->next) {

    if (ptr->state & IS_VISIBLE) {

      hints = (layout = ptr->layout)->GetLayoutHints();
      csize      = ptr->frame->GetDefaultSize();
      pad_left   = layout->GetPadLeft();
      pad_top    = layout->GetPadTop();
      pad_right  = layout->GetPadRight();
      pad_bottom = layout->GetPadBottom();

      if (hints & LHINTS_BOTTOM)
	y = msize.h - bw - csize.h - pad_bottom;
      else if (hints & LHINTS_CENTER_Y)
	y = (msize.h - (bw << 1) - csize.h) >> 1;
      else // LHINTS_TOP by default
	y = pad_top + bw;

      if (hints & LHINTS_EXPAND_Y) {
	size.h = msize.h - (bw << 1) - pad_top - pad_bottom;
	y = pad_top + bw;
      } else {
	size.h = csize.h;
      }

      if (hints & LHINTS_EXPAND_X) {
	if ((remain / nb_expand) >= exp_max)
	  size.w = (remain / nb_expand) - pad_left - pad_right;
	else
	  size.w = csize.w + (remain - exp) / nb_expand;
      } else {
	size.w = csize.w;
	if (hints & LHINTS_CENTER_X) {
	  if ((remain / nb_expand) >= exp_max)
	    extra_space = ((remain / nb_expand) - pad_left - pad_right - size.w) >> 1;
	  else
	    extra_space = ((remain - exp) / nb_expand) >> 1;
	  x += extra_space;
	  left += extra_space;
	}
      }

      if (hints & LHINTS_RIGHT) {
	x = right - size.w - pad_right;
	right -= size.w + pad_left + pad_right;
      } else { // defaults to LHINTS_LEFT
	x = left + pad_left;
	left += size.w + pad_left + pad_right;
      }

      if (hints & LHINTS_CENTER_X)
	left += extra_space;

      ptr->frame->MoveResize(x, y, size.w, size.h);
    }
  }
}

ODimension OHorizontalLayout::GetDefaultSize() const {
  SListFrameElt *ptr;
  ODimension size(0,0), msize = _main->GetSize(), csize;
  int options = _main->GetOptions();

  if ((options & FIXED_WIDTH) && (options & FIXED_HEIGHT))
    return msize;

  for (ptr=*_list; ptr != NULL; ptr=ptr->next) {
    if (ptr->state & IS_VISIBLE) {
      csize = ptr->frame->GetDefaultSize();
      size.w += csize.w + ptr->layout->GetPadLeft() +
                          ptr->layout->GetPadRight();
      size.h = max(size.h, csize.h + ptr->layout->GetPadTop() +
                                     ptr->layout->GetPadBottom());
    }
  }

  size.w += _main->GetBorderWidth() << 1;
  size.h += _main->GetBorderWidth() << 1;

  if (options & FIXED_WIDTH) size.w = msize.w;
  if (options & FIXED_HEIGHT) size.h = msize.h;

  return size;
}

