// $Id: canvas.cpp,v 1.25 2000/12/21 04:04:38 jcaliff Exp $

#include <kdebug.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <strings.h>
#include <qpainter.h>
#include <qimage.h>
#include <qwmatrix.h>
#include <qclipboard.h>

#include <klocale.h>
#include <kapp.h>

#include <math.h>

#include "canvas.h"
#include "tool.h"

#define CUT_FILL_RGB 0xFFFFFF //white

int Canvas::inst = 0;

Canvas::Canvas(int width, int height, QWidget *parent, const char *name)
  : QWidget(parent, name)
{
  currentTool= 0;
  s= INACTIVE;
  matrix= new QWMatrix;
  zoomed= 0;

  // Create pixmap
  pix= new QPixmap(width, height);
  if (!pix) {
    kdDebug(4400) << "Canvas::Canvas(): Cannot create pixmap\n" << endl;
    exit(1);
  }

  pix->fill(QColor("white"));

  setZoom(100);

  // Set keyboard focus policy
  setFocusPolicy(QWidget::StrongFocus);
  emit sizeChanged();

  Canvas::inst++;
  
}

Canvas::Canvas(const QString & filename, QWidget *parent, const char *name)
  : QWidget(parent, name)
{
  currentTool= 0;
  s= INACTIVE;
  zoomed= 0;
  matrix= new QWMatrix;

  // Create pixmap
  pix= new QPixmap(filename);
  if (!pix) 
  {
    kdDebug(4400) << "Canvas::Canvas(): Cannot create pixmap\n" << endl;
    exit(1);
  }

  resize(pix->width(), pix->height());

  setZoom(100);

  emit sizeChanged();

  // Set keyboard focus policy
  setFocusPolicy(QWidget::StrongFocus);
 
  Canvas::inst++;
  
}

Canvas::~Canvas()
{
  /*
  inst--;
  if ((0 == inst) && (NULL != Canvas::clipboardPix)) {
    delete Canvas::clipboardPix;
    Canvas::clipboardPix = NULL;
  }
  */
}   

bool Canvas::isModified()
{
    return modified_;
}

void Canvas::clearModified()
{
    modified_= false;
}

void Canvas::markModified()
{
    modified_= true;
    kdDebug(4400) << "Canvas: emitting modified()\n" << endl;
    emit modified();
}

void Canvas::setSelection(const QRect &rect)
{
    selection_= rect;
    haveSelection_= true;
    emit selection(true);
}

const QRect &Canvas::selection()
{
    return selection_;
}

void Canvas::clearSelection()
{
    haveSelection_= false;
    selection_= QRect(0,0,0,0);
    emit selection(false);
}

QPixmap *Canvas::selectionData()
{
  QPixmap *p;

  if (haveSelection_) 
  {
    p= new QPixmap(selection_.width(), selection_.height());
    bitBlt(p, 0, 0, pix,
	   selection_.left(), selection_.top(),
	   selection_.width(), selection_.height(),
	   CopyROP, true);
  }
  else
    p= 0;

  return p;
}

// -------- CUT / COPY / PASTE ------------

void 
Canvas::cut()
{
  if (haveSelection_) {

    const QColor c((QRgb)CUT_FILL_RGB);
    
    // copy the selection and fill the 
    // copied rect with CUT_FILL_RGB, mostly white, see definition above
    
    copy(true);
    QPixmap p(selection_.size());

    //p.fill(c);
    p.fill(QColor("white"));
    
    bitBlt(pix, selection_.left(), selection_.top(), 
           &p, 0, 0, p.width(), p.height(), CopyROP);
           
    clearSelection();
    markModified();
    updateZoomed();
    repaint(0);
  }
}


void 
Canvas::copy(bool andCut)
{
  if (haveSelection_) 
  {
    int left = selection_.left();
    int top  = selection_.top();
    int wid  = selection_.right() - selection_.left();
    int hgt  = selection_.bottom() - selection_.top();
    
    kdDebug(0) << "left: " << left << " top: " << top << " wid: " << wid << " hgt: " << hgt << endl;   

    clipboardPix.resize(wid, hgt);    
    bitBlt(&clipboardPix, 0, 0, pix, left, top, wid, hgt, CopyROP);
    kapp->clipboard()->setPixmap(clipboardPix); 
    
    if(!andCut) clearSelection();
    emit clipboard(true);
  }
}


void
Canvas::paste()
{
    if(100 != zoom())
    {
        kdDebug(0) << "Cut/Copy/Paste not implemented in zoom mode!" << endl;
    }	

    bitBlt(pix, 0, 0, &clipboardPix, 
        0, 0, clipboardPix.width(), clipboardPix.height() , CopyROP);  

    markModified();
}


// ---------- ZOOM ------------------------

void Canvas::setZoom(int z)
{
  QPainter p;
  int w, h;

  zoomFactor= z;
  matrix->reset();
  matrix->scale((float) z/100, (float) z/100);

  if (zoomed != 0)
    delete zoomed;

  w= (int) (pix->width()* ((float) zoomFactor/100));
  h= (int) (pix->height()*((float) zoomFactor/100));

  zoomed= new QPixmap(w, h);
  zoomed->fill(QColor("white"));

  p.begin(zoomed);
  p.setWorldMatrix(*matrix);
  p.drawPixmap(0,0,*pix);
  p.end();

  if ((w != width()) || (h != height())) {
    resize(w,h);
    emit sizeChanged();
  }
  repaint(0);
}

void Canvas::updateZoomed()
{
  QPainter p;
/*  int w, h; */

  zoomed->fill(QColor("white"));

  p.begin(zoomed);
  p.setWorldMatrix(*matrix);
  p.drawPixmap(0,0,*pix);
  p.end();

  repaint(0);
}

/*
    The offscreen clipboard pixmap must also be adjusted to
    zoom factor for pasting to a zoomed canvas pixmap
*/

void Canvas::updateZoomedClipboard()
{
  if (!clipboardPix.isNull())
  {      
    QPainter p;

    int clipWid = clipboardPix.width();
    int clipHgt = clipboardPix.height();
    
    int newClipWid = (int) (clipWid * ((float) zoomFactor/100));
    int newClipHgt = (int) (clipHgt * ((float) zoomFactor/100));
        
    /* make copy of clipboard data to temporary pixmap */
    QPixmap *zc = new QPixmap(clipWid, clipHgt);    
    zc->fill(QColor("white"));
    p.begin(zc);
    p.drawPixmap(0,0, clipboardPix);
    p.end();

    /* adjust clipboard size to zoom factor */
    clipboardPix.resize(QSize(newClipWid, newClipHgt));
    clipboardPix.fill(QColor("white"));

    /* copy to adjusted clipboard using matrix */
    p.begin(&clipboardPix);
    p.setWorldMatrix(*matrix);
    p.drawPixmap(0,0,*zc);
    p.end();
    
    /* clean up */
    delete zc;
  }
}

bool Canvas::hasClipboardData()
{
    QPixmap cPixmap = kapp->clipboard()->pixmap();
    return (cPixmap.isNull() ? false : true);
}

int Canvas::zoom()
{
  return zoomFactor;
}

void Canvas::activate(Tool *t)
{
  assert(!isActive());
  currentTool= t;
  s= ACTIVE;
}

void Canvas::deactivate()
{
  assert(isActive());
  s= INACTIVE;
  currentTool= 0;
}

QPixmap *Canvas::pixmap()
{
  return pix;
}

QPixmap *Canvas::zoomedPixmap()
{
  return zoomed;
}

void Canvas::setPixmap(QPixmap *px)
{
    QPainter p;
    int w, h;

    *pix= *px;
    emit pixmapChanged(pix);

    delete zoomed;

    w = (int) (px->width()* ((float) zoomFactor/100));
    h = (int) (px->height()*((float) zoomFactor/100));

    zoomed = new QPixmap(w, h);

    p.begin(zoomed);
    p.setWorldMatrix(*matrix);
    p.drawPixmap(0,0,*pix);
    p.end();

    if ((w != width()) || (h != height())) 
    {
        resize(w,h);
        emit sizeChanged();
    }
    repaint(0);
}

void Canvas::setDepth(int d)
{
  QImage i;
  QPixmap *px;

  assert((d == 1) || (d == 4) || (d == 8) || 
	 (d == 15) || (d == 16) ||
	 (d == 24) || (d == 32));

  if (d != pix->depth()) {
    i= pix->convertToImage();
    i.convertDepth(d);
    px= new QPixmap(pix->width(), pix->height(), d);
    *px= i;
    setPixmap(px);
    emit pixmapChanged(px);
    markModified();
    delete px;
  }
}

void Canvas::resizeImage(int w, int h)
{
  QWMatrix matrix;
  QPainter p;

  if ((w != pix->width()) || (h != pix->height())) {
    QPixmap *newpix= new QPixmap(w, h);
    matrix.scale((float) w/pix->width(), (float) h/pix->height());
    p.begin(newpix);
    p.setWorldMatrix(matrix);
    p.drawPixmap(0,0,*pix);
    p.end();

    delete pix;
    pix= newpix;
    setZoom(zoom());
    emit pixmapChanged(pix);
    markModified();
  }
  repaint(0);
}

/* 
    paint Event is the ONLY place where the canvas is
    updated, normally, by copying the offscreen zoomed pixmap
    to the canvas    
*/

void Canvas::paintEvent(QPaintEvent *)
{
  bitBlt(this, 0, 0, zoomed);
}

void Canvas::mousePressEvent(QMouseEvent *e)
{
kdDebug(4400) << "Canvas::mousePressEvent() redirector called\n" << endl;
  if (isActive())
    currentTool->mousePressEvent(e);
} 

void Canvas::mouseMoveEvent(QMouseEvent *e)
{
  if (isActive())
    currentTool->mouseMoveEvent(e);
}

void Canvas::mouseReleaseEvent(QMouseEvent *e)
{
kdDebug(4400) << "Canvas::mouseReleaseEvent() redirector called\n" << endl;
  if (isActive())
    currentTool->mouseReleaseEvent(e);
}

bool Canvas::isActive()
{
  if (s == ACTIVE)
    return true;
  else
    return false;
}

bool Canvas::load(const QString & filename, const char *format)
{
  bool s;
  QPixmap p;
  QPixmap q; // Fix UMR when reading transparent pixels (they hold junk)

  if (!format) 
  { 
    s = p.load(filename);
  } 
  else 
  {
    s = p.load(filename, format);
  }

  if (s) 
  {
    q.resize(p.size());
    q.fill(QColor("white"));
    bitBlt(&q, 0, 0, &p);
    setPixmap(&q);
    emit pixmapChanged(pix);
  }

  repaint(0);

  return s;
}

bool Canvas::save(const QString & filename, const char *format)
{
  bool s;

kdDebug(4400) << "Canvas::save() file= " << filename << ", format= " << format << "\n" << endl;

  s= pix->save(filename, format);

kdDebug(4400) << "Canvas::save() returning " << s << "\n" << endl;

  return s;
}

void Canvas::keyPressEvent(QKeyEvent *e)
{
  //kdDebug(4400) << "Canvas::keyPressEvent() redirector called\n" << endl;
  if (isActive())
    currentTool->keyPressEvent(e);
}


void Canvas::keyReleaseEvent(QKeyEvent *e)
{
  //kdDebug(4400) << "Canvas::keyReleaseEvent() redirector called\n" << endl;
  if (isActive())
    currentTool->keyReleaseEvent(e);
}


#include "canvas.moc"
