/* -*- C++ -*-
 * This file implements a widget for navigating through sets of data.
 * 
 * the K Desktop Environment
 * copyright:  (C) Mirko Sucker, 1999
 * mail to:    Mirko Sucker <mirko.sucker@unibw-hamburg.de>
 * requires:   recent C++-compiler, at least Qt 1.4, STL
 
 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; see the file COPYING.LIB.  If not, write to
 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.
 
 * $Revision: 1.5 $
 */
#include "widget_datanavigator.h"
#include <qlineedit.h>
#include <qspinbox.h>
#include <qcombobox.h>
#include <qstringlist.h>
#include <qpushbutton.h>
#include <kiconloader.h>
#include <kapp.h>
#include <kdebug.h>

#include "widget_datanavigator.moc"

KDataNavigator::KDataNavigator(QWidget* parent, const char* name)
  : QFrame(parent, name),
    buttonFirst(new QPushButton(this)),
    buttonPrevious(new QPushButton(this)),
    buttonNext(new QPushButton(this)),
    buttonLast(new QPushButton(this)),
    buttonNew(new QPushButton(this)),
    leIndex(new QLineEdit(this)),
    sbIndex(new QSpinBox(this)),
    cbIndex(new QComboBox(this)),
    min(0),
    max(0),
    current(0),
    mode(Manual),
    buttonNewVisible(true)
{
  // ###########################################################################
  QPushButton* buttons[]= {
    buttonFirst, buttonPrevious, buttonNext, buttonLast, buttonNew
  };
  const char* icons[]= {
    // "2leftarrow", "1leftarrow", "1rightarrow", "2rightarrow", "filenew2"
    "start", "back", "forward", "finish", "filenew2"
  };
  int count;
  QPixmap pixmap;
  // -----
  if(buttonFirst==0 || buttonPrevious==0 || buttonNext==0 || buttonLast==0 ||
     buttonNew==0 || leIndex==0 || sbIndex==0 || cbIndex==0)
    {
      // possibly throw exception when they are completely implemented
    }
  leIndex->setEnabled(false);
  for(count=0; count<5; ++count)
    {
      pixmap=BarIcon(icons[count]);
      if(!pixmap.isNull())
	{
	  buttons[count]->setPixmap(pixmap);
	}
    }
  // -----
  connect(buttonFirst, SIGNAL(clicked()), SLOT(buttonFirstClicked()));
  connect(buttonPrevious, SIGNAL(clicked()), SLOT(buttonPreviousClicked()));
  connect(buttonNext, SIGNAL(clicked()), SLOT(buttonNextClicked()));
  connect(buttonLast, SIGNAL(clicked()), SLOT(buttonLastClicked()));
  connect(buttonNew, SIGNAL(clicked()), SLOT(buttonNewClicked()));
  connect(leIndex, SIGNAL(textChanged(const QString&)),
	  SLOT(leTextChanged(const QString&)));
  connect(cbIndex, SIGNAL(activated(int)), SLOT(cbItemSelected(int)));
  connect(sbIndex, SIGNAL(valueChanged(int)), SLOT(sbValueChanged(int)));
  // -----
  setFrameStyle(QFrame::Box | QFrame::Plain);
  setLineWidth(1);
  enableButtons();
  // ###########################################################################
}

KDataNavigator::~KDataNavigator()
{
  // ###########################################################################
  // ###########################################################################
}

void
KDataNavigator::resizeEvent(QResizeEvent*)
{
  // ###########################################################################
  setLayout();
  // ###########################################################################
}

void
KDataNavigator::setLayout()
{
  // ###########################################################################
  // ----- sorry for the bad construct:
  //       the layout management should be moved to QLayout to be more readable:
  QWidget *widgets[]= {
    buttonFirst, buttonPrevious,
    mode==Manual ? leIndex : (mode==List
			      ? (QWidget*)cbIndex
			      : (QWidget*)sbIndex), 
    buttonNext, buttonLast, buttonNew
  };
  const int NoOfElements=sizeof(widgets)/sizeof(widgets[0]);
  const int NoOfButtons= buttonNewVisible ? 5 : 4;
  int count, w=0, h=0;
  // ----- find size hints of buttons in h and w:
  for(count=0; count<NoOfElements; ++count)
    {
      h=QMAX(h, widgets[count]->sizeHint().height());
      if(count!=2) // except combobox
	{
	  w=QMAX(w, widgets[count]->sizeHint().width());
	}
    }
  // ----- calculate heights and widths finally:
  const int ButtonHeight= h>0 ? h : height()-2*frameWidth();
  const int ButtonWidth= w>0 ? w : h;
  int widths[]= {
    ButtonWidth, ButtonWidth,
    width()-2*frameWidth()-(NoOfButtons)*ButtonWidth,
    ButtonWidth, ButtonWidth, buttonNewVisible ? ButtonWidth : 0
  };
  h=height()-2*frameWidth();
  if(h>ButtonHeight) h=ButtonHeight;
  int heights[]= { h, h, widgets[2]->sizeHint().height(), h, h, h };
  int cx, offset;
  // -----
  cx=frameWidth();
  for(count=0; count<NoOfElements; ++count)
    {
      if(count==2)
	{
	  offset=(height()-heights[count])/2;
	  // this will look ugly as the combo does not fit:
	  if(offset<=0) offset=frameWidth();
	} else {
	  offset=frameWidth();
	}
      widgets[count]->setGeometry(cx, offset, widths[count], heights[count]);
      cx+=widths[count];
    }  
  // ###########################################################################
}


QSize
KDataNavigator::sizeHint()
{
  // ###########################################################################
  const int ButtonWidth=20; // KButtons do not seem to report it
  const int NoOfButtons= buttonNewVisible ? 5 : 4;
  // -----
  switch(mode)
    {
    case Manual:
      return QSize((NoOfButtons+6)*ButtonWidth+2*frameWidth(),
		   ButtonWidth+2*frameWidth());
    case List:
      return QSize((NoOfButtons+8)*ButtonWidth+2*frameWidth(),
		   ButtonWidth+2*frameWidth());
    default:
      return QSize((NoOfButtons+4)*ButtonWidth+2*frameWidth(),
		   ButtonWidth+2*frameWidth());
    }
  // ###########################################################################
}

void
KDataNavigator::buttonFirstClicked()
{
  // ###########################################################################
  switch(mode)
    {
    case Manual: // the most easy case: do nothing
      break;
    case Index:
      if(min<current)
	{
	  current=min;
	  emit(itemSelected(current));
	  sbIndex->setValue(current);
	  enableButtons();
	} else {
	  kapp->beep(); // cannot happen
	}
      break;
    case List:
      if(current>0 && cbIndex->count()>0)
	{
	  current=0;
	  cbIndex->setCurrentItem(current);
	  emit(itemSelected(current));
	  enableButtons();
	}
      break;
    }
  emit(first());
  // ###########################################################################
}

void
KDataNavigator::buttonPreviousClicked()
{
  // ###########################################################################
  switch(mode)
    {
    case Manual:
      break;
    case Index:
      if(min<current)
	{
	  --current;
	  emit(itemSelected(current));
	  sbIndex->setValue(current);
	  enableButtons();
	} else {
	  kapp->beep();
	}
      break;
    case List:
      if(current>0 && cbIndex->count()>0)
	{
	  --current;
	  cbIndex->setCurrentItem(current);
	  emit(itemSelected(current));
	  enableButtons();
	}  
      break;
    }
  emit(previous());
  // ###########################################################################
}

void
KDataNavigator::buttonNextClicked()
{
  // ###########################################################################
  switch(mode)
    {
    case Manual:
      break;
    case Index:
      if(current<max)
	{
	  ++current;
	  emit(itemSelected(current));
	  sbIndex->setValue(current);
	  enableButtons();
	} else {
	  kapp->beep();
	}
      break;
    case List:
      if(current<cbIndex->count()-1 && cbIndex->count()>0)
	{
	  ++current;
	  cbIndex->setCurrentItem(current);
	  emit(itemSelected(current));
	  enableButtons();
	}      
      break;
    }
  emit(next());
  // ###########################################################################
}

void
KDataNavigator::buttonLastClicked()
{
  // ###########################################################################
  switch(mode)
    {
    case Manual:
      break;
    case Index:
      if(current<max)
	{
	  current=max;
	  emit(itemSelected(current));
	  sbIndex->setValue(current);
	  enableButtons();
	} else {
	  kapp->beep();
	}
      break;
    case List:
      if(current<cbIndex->count()-1 && cbIndex->count()>0)
	{
	  current=cbIndex->count()-1;
	  cbIndex->setCurrentItem(current);
	  emit(itemSelected(current));
	  enableButtons();
	}      
      break;
    }
  emit(last());
  // ###########################################################################
}

void
KDataNavigator::buttonNewClicked()
{
  // ###########################################################################
  emit(newItem());
  // ###########################################################################
}

void
KDataNavigator::setText(const char* c)
{
  // ###########################################################################
  setMode(Manual);
  leIndex->setText(c);
  // ###########################################################################
}

void
KDataNavigator::setIndexMode(int begin, int end, int cur)
{
  // ###########################################################################
  min=begin;
  max=end;
  current=cur;
  sbIndex->setRange(min, max);
  if(min<=current && current<=max)
    {
      sbIndex->setValue(current);
    } else {
      sbIndex->setValue(min);
    }
  leIndex->hide();
  cbIndex->hide();
  sbIndex->show();
  // -----
  mode=Index;
  enableButtons();
  // ###########################################################################
}

void
KDataNavigator::showButtonNew(bool state)
{
  // ###########################################################################
  if(state)
    {
      buttonNew->show();
    } else {
      buttonNew->hide();
    }  
  buttonNewVisible=state;
  // -----
  setLayout();
  // ###########################################################################
}

void
KDataNavigator::enableButtonFirst(bool state)
{
  // ###########################################################################
  buttonFirst->setEnabled(state);
  // ###########################################################################
}
  
void
KDataNavigator::enableButtonNext(bool state)
{
  // ###########################################################################
  buttonNext->setEnabled(state);
  // ###########################################################################
}
  
void
KDataNavigator::enableButtonPrevious(bool state)
{
  // ###########################################################################
  buttonPrevious->setEnabled(state);
  // ###########################################################################
}
  
void
KDataNavigator::enableButtonLast(bool state)
{
  // ###########################################################################
  buttonLast->setEnabled(state);
  // ###########################################################################
}
  
void
KDataNavigator::enableButtonNew(bool state)
{
  // ###########################################################################
  buttonNew->setEnabled(state);
  // ###########################################################################
}

void
KDataNavigator::enableButtons()
{
  // ###########################################################################
  switch(mode)
    {
    case Manual:
      break;
    case Index:
      buttonFirst->setEnabled(current!=min);
      buttonPrevious->setEnabled(current!=min);
      buttonNext->setEnabled(current!=max);
      buttonLast->setEnabled(current!=max);
      break;
    case List:
      buttonFirst->setEnabled(current!=0);
      buttonPrevious->setEnabled(current!=0);
      buttonNext->setEnabled(current!=cbIndex->count()-1 && cbIndex->count()>0);
      buttonLast->setEnabled(current!=cbIndex->count()-1 && cbIndex->count()>0);
      cbIndex->setEnabled(cbIndex->count()>0);
      break;
    }
  // ###########################################################################
}
  
void
KDataNavigator::leTextChanged(const QString& s)
{
  // ###########################################################################
  emit(textChanged(s));
  // ###########################################################################
}

void
KDataNavigator::sbValueChanged(int i)
{
  // ###########################################################################
  if(min<=i && i<=max)
    {
      current=i;
      enableButtons();
      emit(itemSelected(i));
    }
  // ###########################################################################
}

void
KDataNavigator::cbItemSelected(int i)
{
  // ###########################################################################
  current=i;
  enableButtons();
  emit(itemSelected(i));
  // ###########################################################################
}

void
KDataNavigator::setMode(Modes m)
{
  // ###########################################################################
  mode=m;
  switch(mode)
    {
    case Manual:
      leIndex->show();
      sbIndex->hide();
      cbIndex->hide();
      buttonFirst->setEnabled(true);
      buttonPrevious->setEnabled(true);
      buttonNext->setEnabled(true);
      buttonLast->setEnabled(true);
      break;
    case Index:
      leIndex->hide();
      sbIndex->show();
      cbIndex->hide();
      break;
    case List:
      leIndex->hide();
      sbIndex->hide();
      cbIndex->show();
      break;
    }
  enableButtons();
  setLayout();
  // ###########################################################################
}

void
KDataNavigator::setList(QStringList* strings)
{
  register bool GUARD; GUARD=false;
  // ###########################################################################
  kDebugInfo(GUARD, 0, "KDataNavigator::setList: called, %i items.",
	     strings->count());
  // -----
  if(cbIndex->count()>0) cbIndex->clear();
  cbIndex->insertStringList(*strings);
  if(cbIndex->count()>0)
    {
      cbIndex->setCurrentItem(0);
      current=0;
    }
  setMode(List);
  // ###########################################################################
}
