/* This file is part of the KDE project
   Copyright (C) 2001 Wilco Greven <greven@kde.org>
   Copyright (C) 2002-2004 Stefan Kebekus <kebekus@kde.org>

   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.
*/

#include <kdebug.h>
#include <qapplication.h>
#include <qcursor.h>
#include <qimage.h>
#include <qpainter.h>
#include <qrect.h>
#include <math.h>

#include "centeringScrollview.h"

CenteringScrollview::CenteringScrollview( QWidget* parent, const char* name )
  : QScrollView( parent, name)
{
  widgetList = 0;
  setFocusPolicy( QWidget::StrongFocus );
  viewport()->setFocusPolicy( QWidget::WheelFocus );

  setResizePolicy(QScrollView::Manual);

  setVScrollBarMode(QScrollView::Auto);
  setHScrollBarMode(QScrollView::Auto);

  viewport()->setBackgroundMode(QWidget::PaletteMid);

  URShadow.resize(4,4);
  BRShadow.resize(4,4);
  BLShadow.resize(4,4);
  enableClipper(true);
  nrCols = 2;
  nrRows = 1;
  continuousViewmode = true;
  fullScreen = false;
}


void CenteringScrollview::addChild( QPtrVector<QWidget> *wdgList )
{
  if( wdgList == 0 ) {
    kdError(4300) << "CenteringScrollview::addChild(...) called with invalid arguments" << endl;
    return;
  }

  widgetList = wdgList;
  for(Q_UINT16 i=0; i<widgetList->size(); i++) 
    connect(widgetList->at(i), SIGNAL(resized()), this, SLOT(centerContents()));
  centerContents();
}


void CenteringScrollview::addChild( QWidget *widget )
{
  widgetListIfOldAddChildIsUsed.resize(1);
  widgetListIfOldAddChildIsUsed.insert(0, widget);
  addChild(&widgetListIfOldAddChildIsUsed);
}


bool CenteringScrollview::atTop() const
{
  return verticalScrollBar()->value() == verticalScrollBar()->minValue();
}


bool CenteringScrollview::atBottom() const 
{
  return verticalScrollBar()->value() == verticalScrollBar()->maxValue();
}


bool CenteringScrollview::readUp()
{
  if( atTop() )
    return false;
  else {
    int newValue = QMAX( verticalScrollBar()->value() - (int)(height() * 0.95),
			 verticalScrollBar()->minValue() );
    verticalScrollBar()->setValue( newValue );
    return true;
  }
}


bool CenteringScrollview::readDown()
{   
  if( atBottom() )
    return false;
  else {
    int newValue = QMIN( verticalScrollBar()->value() + (int)(height() * 0.95),
			 verticalScrollBar()->maxValue() );
    verticalScrollBar()->setValue( newValue );
    return true;
  }
}


void CenteringScrollview::scrollRight()
{
    horizontalScrollBar()->addLine();
}

void CenteringScrollview::scrollLeft()
{
    horizontalScrollBar()->subtractLine();
}

void CenteringScrollview::scrollDown()
{
    verticalScrollBar()->addLine();
}

void CenteringScrollview::scrollUp()
{
    verticalScrollBar()->subtractLine();
}

void CenteringScrollview::scrollBottom()
{
    verticalScrollBar()->setValue( verticalScrollBar()->maxValue() );
}

void CenteringScrollview::scrollTop()
{
    verticalScrollBar()->setValue( verticalScrollBar()->minValue() );
}

void CenteringScrollview::keyPressEvent( QKeyEvent* e )
{   
    switch ( e->key() ) {
    case Key_Up:
	scrollUp();
	break;
    case Key_Down:
	scrollDown();
	break;
    case Key_Left:
	scrollLeft();
	break;
    case Key_Right:
	scrollRight();
	break;
    default:
	e->ignore();
	return;
    }
    e->accept();
}

void CenteringScrollview::contentsMousePressEvent( QMouseEvent* e )
{
  if (e->button() == LeftButton)
  {
    setCursor(Qt::SizeAllCursor);
    dragGrabPos = e->globalPos();
  }
  else
  {
    setCursor(Qt::arrowCursor);
  }
}

void CenteringScrollview::contentsMouseReleaseEvent( QMouseEvent* )
{
  setCursor(Qt::arrowCursor);
}

void CenteringScrollview::contentsMouseMoveEvent( QMouseEvent* e )
{
  QPoint newPos = e->globalPos();

  if (e->state() == LeftButton)
  {
    QPoint delta = dragGrabPos - newPos;
    scrollBy(delta.x(), delta.y());
  }
  dragGrabPos = newPos;
}

void CenteringScrollview::viewportResizeEvent( QResizeEvent* e )
{
  QScrollView::viewportResizeEvent( e );
  emit viewSizeChanged( viewport()->size() );
  centerContents();
}

void CenteringScrollview::setNrColumns( Q_UINT8 cols )
{
  nrCols = cols;
  centerContents();
}

void CenteringScrollview::setNrRows( Q_UINT8 rows )
{
  nrRows = rows;
}

void CenteringScrollview::setContinuousViewMode(bool continuous)
{
  continuousViewmode = continuous;
  kdDebug(1223) << "ScrollView cont = " << continuous << endl;
}

bool CenteringScrollview::singlePageFullScreenMode()
{
  return (nrCols == 1 && nrRows == 1 && !continuousViewmode && fullScreen);
}

void CenteringScrollview::slotShowScrollbars(bool status)
{
  if (status == true) {
    setVScrollBarMode(QScrollView::Auto);
    setHScrollBarMode(QScrollView::Auto);
  } else {
    setVScrollBarMode(QScrollView::AlwaysOff);
    setHScrollBarMode(QScrollView::AlwaysOff);
  }  

  // Remember the state of the scrollbars so that we can restore
  // scrollbar visibility when we switch back from fullscreen mode to
  // normal mode
  showScrollBars = status;
}

void CenteringScrollview::setFullScreenMode(bool fullScreen)
{ 
  this -> fullScreen = fullScreen; 
  if (fullScreen == true) 
  {
    setVScrollBarMode(QScrollView::AlwaysOff);
    setHScrollBarMode(QScrollView::AlwaysOff);
    oldFrameStyle = frameStyle();
    setFrameStyle(QFrame::NoFrame);
    backgroundColor = viewport()->paletteBackgroundColor();
    if (singlePageFullScreenMode())
    {
      viewport()->setPaletteBackgroundColor( Qt::black ) ;
    }
    // repaintContents(true);
  } 
  else 
  {
    viewport()->setPaletteBackgroundColor( backgroundColor ) ;
    slotShowScrollbars(showScrollBars);
    setFrameStyle(oldFrameStyle);
  }
}

void CenteringScrollview::centerContents()
{
  // Paranoid safety check
  if (widgetList == 0)
    return;

  // If there are no widgets, e.g. because the last widget has been
  // removed, the matter is easy: set the contents size to 0. If there
  // are no widgets because previously existing widgets were removed
  // (we detect that by looking at the contentsWidth and -Height), we
  // make sure to update the viewport so that old shadows will be
  // removed.
  if (widgetList->isEmpty()) {
    if ((contentsWidth() != 0) || (contentsHeight() != 0)) {
      QScrollView::resizeContents(0,0);
      viewport()->update();
      // Process pending events. In particular, re-draw the the
      // viewport now. This is useful as this method is often called
      // when widgets are removed, because a new file is
      // loaded. Loading a file may take a second, and we make sure
      // here that the user is not left with a garbled looking
      // viewport meanwhile.
      qApp->processEvents();
    }
    return;
  }

  // Ok, now we are in a situation where we do have some widgets that
  // shall be centered.
  int distance = distanceBetweenWidgets;
  if (singlePageFullScreenMode())
  {
    // In single page fullscreen mode we don't want a margin around the pages
    distance = 0;
  }

  QMemArray<Q_UINT32> colWidth(nrCols);
  for(Q_UINT8 i=0; i<colWidth.size(); i++)
    colWidth[i] = 0;

  Q_UINT16 numRows;
  if(nrCols <= 2)
  {
    numRows = (widgetList->size()+2*nrCols-2) / nrCols;
  }
  else
  {
    numRows = (Q_INT16)ceil(((double)widgetList->size()) / nrCols);
  }

  QMemArray<Q_UINT32> rowHeight(numRows);
  for(Q_UINT16 i=0; i<rowHeight.size(); i++)
    rowHeight[i] = 0;

  // Now find the widths and heights of the columns
  for(Q_UINT16 i=0; i<widgetList->size(); i++) 
  {
    Q_UINT8 col;
    Q_UINT16 row;

    if (nrCols == 2) {
      // In two-column display, start with the right column
      col = (i+1+nrCols) % nrCols;
      row = (i+1+nrCols) / nrCols - 1;
    } else {
      col = (i+nrCols) % nrCols;
      row = (i+nrCols) / nrCols - 1;
    }

    colWidth[col] = QMAX(colWidth[col], (Q_UINT32)widgetList->at(i)->width());
    rowHeight[row] = QMAX(rowHeight[row], (Q_UINT32)widgetList->at(i)->height());
  }

  // Calculate the total width and height of the display
  Q_UINT32 totalHeight = 0;
  for(Q_UINT16 i=0; i<rowHeight.size(); i++)
    totalHeight += rowHeight[i];

  totalHeight += (numRows+1)*distance;
  Q_UINT32 totalWidth = 0;
  for(Q_UINT8 i=0; i<colWidth.size(); i++)
    totalWidth += colWidth[i];

  totalWidth += (nrCols+1)*distance;
  QSize newViewportSize = viewportSize( totalWidth, totalHeight );
  Q_UINT32 centeringLeft = 0;
  if( (Q_UINT32)newViewportSize.width() > totalWidth )
    centeringLeft = ( newViewportSize.width() - totalWidth )/2;
  Q_UINT32 centeringTop = 0;
  if( (Q_UINT32)newViewportSize.height() > totalHeight )
    centeringTop = ( newViewportSize.height() - totalHeight)/2;

  // Finally, calculate the left and top coordinates of each row and
  // column, respectively
  QMemArray<Q_UINT32> colLeft(nrCols);
  colLeft[0] = distance;
  for(Q_UINT8 i=1; i<colLeft.size(); i++)
    colLeft[i] = colLeft[i-1]+colWidth[i-1]+distance;

  QMemArray<Q_UINT32> rowTop(numRows);
  rowTop[0] = distance;
  for(Q_UINT16 i=1; i<rowTop.size(); i++)
    rowTop[i] = rowTop[i-1]+rowHeight[i-1]+distance;

  for(Q_UINT16 i=0; i<widgetList->size(); i++) 
  {
    Q_UINT8 col;
    Q_UINT16 row;
    if (nrCols == 2)
    {
      // In two column-mode start with the right column.
      col = (i+nrCols-1) % nrCols;
      row = (i+nrCols-1) / nrCols;
    }
    else
    {
      col = (i+nrCols) % nrCols;
      row = i / nrCols;
    }
    moveChild( widgetList->at(i), centeringLeft + colLeft[col], centeringTop+rowTop[row] );
  }

  // Re-draw the viewport background so that old shadows are removed
  if (((Q_UINT32)contentsWidth() != totalWidth) || ((Q_UINT32)contentsHeight() != totalHeight))
    resizeContents(totalWidth,totalHeight);

  viewport()->update();
}


void CenteringScrollview::contentsWheelEvent ( QWheelEvent * e )
{
  emit(wheelEventReceived(e));
}


// The following tables store grey values for roundish shadow
// corners. They were shamelessly stolen from kdelibs/kdefx/kstyle.cpp

const int bottom_right_corner[16] =
  { 61, 71, 85, 95,
    71, 78, 89, 96,
    85, 89, 95, 98,
    95, 97, 98, 99 };
const int bottom_left_corner[16] =
  { 95, 85, 71, 61,
    97, 89, 78, 71,
    98, 95, 89, 85,
    99, 98, 96, 95 };
const int shadow_strip[4] =
  { 56, 67, 83, 94 };


void CenteringScrollview::drawContents( QPainter *p, int, int, int, int )
{
  // Safety check
  if ((widgetList == 0) ||  (widgetList->isEmpty()))
    return;

  // Shadows are only drawn if a child widget exists, is shown and not
  // 0x0 size, and if whe are not in single page fullscreen mode
  if ( (widgetList != 0) && (!singlePageFullScreenMode()) ) {
    QColor backgroundColor = viewport()->paletteBackgroundColor();

    // (Re-)generate the Pixmaps for the shadow corners, if necessary
    if (backgroundColor != viewportBackgroundColorForCorners) {
      viewportBackgroundColorForCorners = backgroundColor;
      QImage tmp(4, 4, 32);

      for(int x=0; x<4; x++)
        for(int y=0; y<4; y++)
          tmp.setPixel(x, y, backgroundColor.light(bottom_right_corner[x+4*y]).rgb() );
      BRShadow.convertFromImage(tmp);

      for(int x=0; x<4; x++)
        for(int y=0; y<4; y++)
          tmp.setPixel(x, y, backgroundColor.light(bottom_left_corner[x+4*y]).rgb() );
      BLShadow.convertFromImage(tmp);

      URShadow.convertFromImage(tmp.mirror(true, true));
    }

    // Go through all widgets, but draw frames only about those that
    // are visible.
    QRect visiblRect(contentsX(), contentsY(), visibleWidth(), visibleHeight());
    for(Q_UINT16 widgetNr=0; widgetNr<widgetList->size(); widgetNr++) {
      QWidget *pagePtr = widgetList->at(widgetNr);

      // Paranoid safety check
      if (pagePtr == 0) {
        kdError(4300) << "Safety check failed in void CenteringScrollview::drawContents( QPainter *p, int, int, int, int )" << endl;
        continue;
      }
      Q_INT32 X = childX(pagePtr);
      Q_INT32 Y = childY(pagePtr);
      Q_INT32 W = pagePtr->width();
      Q_INT32 H = pagePtr->height();

      QRect widgetRect(X, Y, W, H );
      if (widgetRect.intersects(visiblRect)) {
        // Draw right and bottom shadows
        for(Q_INT32 i=0; i<4; i++) {
          p->setPen(backgroundColor.light(shadow_strip[i]));
          // Right shadow
          p->drawLine(X+W+i, Y+8, X+W+i, Y+H);
          // Bottom shadow
          p->drawLine(X+8, Y+H+i, X+W, Y+H+i );
        }
        // Draw shadow corners
        p->drawPixmap(X+W, Y+H, BRShadow);
        p->drawPixmap(X+4, Y+H, BLShadow);
        p->drawPixmap(X+W, Y+4, URShadow);
      }
    }
  }
}

void CenteringScrollview::moveViewportToWidget(QWidget* widget)
{
  setContentsPos(childX(widget) - distanceBetweenWidgets, 
                 childY(widget) - distanceBetweenWidgets);
}

#include "centeringScrollview.moc"
