// $Id: kpaint.cpp,v 1.59 2000/06/05 11:04:59 hoelzer Exp $

#include <string.h>
#include <unistd.h>
#include <time.h>

#include <qlayout.h>
#include <qmessagebox.h>
#include <qregexp.h>
#include <qwidget.h>
#include <qsignalmapper.h>

#include <config.h>

#include <kapp.h>
#include <kdebug.h>
#include <kconfig.h>
#include <kfiledialog.h>
#include <kiconloader.h>
#include <kimageio.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <ktmainwindow.h>
#include <ktoolbar.h>
#include <kurl.h>

#include <kaction.h>
#include <kstdaction.h>
#include <kaccel.h>
#include <kstdaccel.h>

#include <kio/netaccess.h>

#include <qscrollview.h>
#include "canvas.h"
#include "version.h"
#include "kpaint.h"
#include "manager.h"
#include "canvassize.h"
#include "palettedialog.h"
#include "infodialog.h"
#include "depthdialog.h"
#include "view.h"
#include "sidebar.h"
#include "colorbar.h"
#include "mainview.h"

/* color definitions in RGB */
#define RED_FILT 0x0000FF
#define GREEN_FILT 0x00FF00
#define BLUE_FILT 0xFF0000
#define DARK_FILT 0x808080

#define WHITE 0xFFFFFF

#define RED (WHITE & RED_FILT)
#define GREEN (WHITE & GREEN_FILT)
#define BLUE (WHITE & BLUE_FILT)
#define CYAN (GREEN | BLUE)
#define MAGENTA (RED | BLUE)
#define YELLOW (RED | GREEN)
#define DARK_RED (DARK_FILT & RED)
#define DARK_GREEN (DARK_FILT & GREEN)
#define DARK_BLUE (DARK_FILT & BLUE)
#define DARK_CYAN (DARK_FILT & CYAN)
#define DARK_MAGENTA (DARK_FILT & MAGENTA)
#define DARK_YELLOW (DARK_FILT & YELLOW)
#define LIGHT_GREY 0xC0C0C0
#define GREY 0xA4A0A0
#define DARK_GREY (DARK_FILT & WHITE)
#define BLACK 0x000000

extern int openwins;

KPaint::KPaint(const char *url) : KTMainWindow()
{
  /*  int w, h; */

  modified= false;
  filename= i18n("untitled") + "." + KImageIO::suffix(KImageIO::types(KImageIO::Writing).first());

  mv = new MainView(this);
  v = mv->getViewport();
  c = mv->getCanvas();
  SideBar *side = mv->getSideBar();

  v->resize(c->size());

  setView( mv );

  man= new Manager(c);
  connect(c, SIGNAL(sizeChanged()), v, SLOT(resizeScrollBars()));
  connect(c, SIGNAL(modified()), this, SLOT(updateCommands()));
  connect(c, SIGNAL(selection(bool)), SLOT(enableEditCutCopy(bool)));
  connect(c, SIGNAL(clipboard(bool)), SLOT(enableEditPaste(bool)));

  /*
  SideBar *side= new SideBar(c, view, "sidebar");
  connect(c, SIGNAL(modified()), side, SLOT(pixmapChanged()));

  
  view->c= v;
  view->s= side;
  */

  connect(side, SIGNAL(lmbColourChanged(const QColor &)),
	  man, SLOT(setLMBcolour(const QColor &)));
  connect(side, SIGNAL(rmbColourChanged(const QColor &)),
	  man, SLOT(setRMBcolour(const QColor &)));

  zoom= 100;
  openwins++;

  initMenus();
 
  initStatus();

  readOptions();

  if (url != 0)
    loadFile(KURL(url));

  allowEditPalette = c->pixmap()->depth() > 8;

  /* add default Colors to colorbar */
  defaultCb = mv->getDefaultColorBar();
  addDefaultColors(*defaultCb);

  updateCommands();

  resize(640,480);
}

KPaint::~KPaint()
{
  delete man;
}

void KPaint::setPixmap(QPixmap *p)
{
  // TODO: Fix setPixmap()
  modified= false;
  url=KURL();
  c->setPixmap(p);
}

int KPaint::exit()
{
  int die= 0;

  if (!(c->isModified())) {
    die= 1;
  }
  else {
    if (QMessageBox::warning(this, i18n("Unsaved Changes"),
			     i18n("You have unsaved changes, you "
				  "will lose them if you close "
				  "this window now."),
			     i18n("Close"), i18n("Cancel"),
			     QString::null, 1, 1)) {
      die= 0;
    }
    else {
      die= 1;
    }
  }
  return die;
}

void KPaint::closeEvent(QCloseEvent *e)
{
  QString proto;

  // Catch if modified
  if (exit()) {
    e->accept();
    openwins--;
  }

  if (openwins == 0)
    kapp->exit(0);
}


void KPaint::updateCommands()
{
  copyAction->setEnabled(c->hasSelection());
  cutAction->setEnabled(c->hasSelection());
  pasteAction->setEnabled(c->hasClipboardData());
  maskAction->setEnabled(false);
  saveAction->setEnabled(c->isModified());
  saveAsAction->setEnabled(c->isModified());
  paletteAction->setEnabled(allowEditPalette);
  pasteImageAction->setEnabled(c->hasClipboardData());
}

void KPaint::readOptions()
{
  KConfig *config = KApplication::kApplication()->config();
  
  config->setGroup( "Appearance" );

  // Read the entries
  toolbarAction->setChecked(config->readBoolEntry("ShowToolsToolBar", true));
  statusbarAction->setChecked(config->readBoolEntry("ShowStatusBar", true));

  updateControls();
}

void KPaint::writeOptions()
{
  KConfig *config = KApplication::kApplication()->config();

  config->setGroup( "Appearance" );

  // Write the entries
  config->writeEntry("ShowToolsToolBar", toolbarAction->isChecked());
  config->writeEntry("ShowStatusBar", statusbarAction->isChecked());
  config->sync();
}

void 
KPaint::enableEditCutCopy(bool e)
{
  debug ("kpaint: %s cut/copy",e?"enable":"disable");
  cutAction->setEnabled(e);
  copyAction->setEnabled(e);
}

void 
KPaint::enableEditPaste(bool e)
{
  debug ("kpaint: %s paste",e?"enable":"disable");
  pasteAction->setEnabled(e);
  pasteImageAction->setEnabled(e);
}

void KPaint::updateControls()
{
  KToolBar *bar = (KToolBar *)child("mainToolBar", "KToolBar");
  if (bar)
    if (toolbarAction->isChecked())
      bar->show();
    else
      bar->hide();
  bar = (KToolBar *)child("toolsToolBar", "KToolBar");
  if (bar)
    if (toolbarAction->isChecked())
      bar->show();
    else
      bar->hide();
  if (statusbarAction->isChecked())
    statusbar->show();
  else
    statusbar->hide();
}

void KPaint::canvasSizeChanged()
{
  QString size;

  size.sprintf("%d x %d", c->width(), c->height());
  statusbar->changeItem(size, ID_FILE_SIZE);
}

void KPaint::initStatus()
{
  QString size;
  //  QFontMetrics fm; (below)
  ToolList tl;
  Tool *t, *maxt;
  uint maxtlen= 0, tmp;

  statusbar= new KStatusBar(this);
  setStatusBar(statusbar);

  /* write the image size */
  size.sprintf("%d x %d", c->width(), c->height());
  statusbar->insertItem(size, ID_FILE_SIZE);

  /* write the color depth */
  size.sprintf(" %d bpp", c->getDepth());
  statusbar->insertItem(size, ID_COLOR_DEPTH);

  /* write the zoomfactor */
  statusbar->insertItem("100%", ID_ZOOM_FACTOR);

  /* write the toolname */
  /* get the max. font length of the toolnames */
  tl = man->getToolList();
  QFontMetrics fm = statusbar->fontMetrics();
  for (t= tl.first(); t != NULL; t= tl.next()) {
    tmp = fm.width(t->getName());
    /* check the tool with the max. name len */
    if ( maxtlen < tmp ) {
      maxtlen = tmp;
      maxt = t;
    }
  }
  /* write the maxlen toolname */
  size = "";
  size += maxt->getName();
  statusbar->insertItem(size, ID_TOOL_NAME);

  /* write the filename */
  statusbar->insertItem(url.path(), ID_FILE_NAME);

  /* update to the current toolname */
  statusbar->changeItem(man->getCurrentTool().getName(), ID_TOOL_NAME);
  man->setStatusBar(statusbar);

  statusbar->show();
}


void KPaint::initMenus()
{
  KAction *action;

  // file menu
  action = KStdAction::openNew(this, SLOT(fileNew()), actionCollection());
  action = KStdAction::open(this, SLOT(fileOpen()), actionCollection());
  saveAction = KStdAction::save(this, SLOT(fileSave()), actionCollection());
  saveAsAction = KStdAction::saveAs(this, SLOT(fileSaveAs()), actionCollection());
  action = KStdAction::quit(this, SLOT(fileExit()), actionCollection());

  // edit menu
  copyAction = KStdAction::copy(this, SLOT(editCopy()), actionCollection());
  cutAction = KStdAction::cut(this, SLOT(editCut()), actionCollection());
  pasteAction = KStdAction::paste(this, SLOT(editPaste()), actionCollection());
  pasteImageAction = new KAction(i18n("Paste &image"), 0, this,
                       SLOT(editPasteImage()), actionCollection(),
                       "edit_paste_image");                                       
  maskAction = new KAction(i18n("&Mask..."), 0, this,
                       SLOT(editMask()), actionCollection(),
                       "edit_mask");                                       
  
  // view menu
  action = KStdAction::zoomIn(this, SLOT(editZoomIn()), actionCollection());
  action = KStdAction::zoomOut(this, SLOT(editZoomOut()), actionCollection());

  // image menu
  action = new KAction(i18n("&Information..."), 0, this,
                       SLOT(imageInfo()), actionCollection(),
                       "image_info");                                       
  action = new KAction(i18n("&Resize..."), 0, this,
                       SLOT(imageResize()), actionCollection(),
                       "image_resize");                                       
  paletteAction = new KAction(i18n("&Edit palette..."), 0, this,
                       SLOT(imageEditPalette()), actionCollection(),
                       "image_palette");                                       
  action = new KAction(i18n("&Change color depth..."), 0, this,
                       SLOT(imageChangeDepth()), actionCollection(),
                       "image_color");                                       
    
  // settings menu
  toolbarAction = KStdAction::showToolbar(this, SLOT(updateControls()), actionCollection());
  statusbarAction = KStdAction::showStatusbar(this, SLOT(updateControls()), actionCollection());
  action = KStdAction::saveOptions(this, SLOT(writeOptions()), actionCollection());

  // window menu
  action = new KAction(i18n("&New window"), 0, this,
                       SLOT(newWindow()), actionCollection(),
                       "window_new");                                       
  action = new KAction(i18n("&Close window"), 0, this,
                       SLOT(closeWindow()), actionCollection(),
                       "window_close");                                       

  // tool menu
  action = new KAction(i18n("&Tool properties..."), 0, this,
                       SLOT(toolProperties()), actionCollection(),
                       "tool_config");                                       

  QSignalMapper *mapper = new QSignalMapper(this);

  ToolList tl = man->getToolList();
  Tool *t;
  int id=0;
  for (t= tl.first(); t != NULL; t= tl.next()) 
    {
      action = new KAction(t->getName(), QIconSet(t->pixmap()), 0, this,
			   SLOT(dummy()), actionCollection(),
			   QString("tool_%1").arg(id));  
      mapper->setMapping(action, id);
      id++;
      connect(action, SIGNAL(activated()), mapper, SLOT(map()));
    }
  
  connect(mapper, SIGNAL(mapped(int)), this, SLOT(setTool(int)));
  
  createGUI("kpaint.rc");
}


void
KPaint::addDefaultColors(ColorBar &cb)
{
  cb.addColor(RED);
  cb.addColor(GREEN);
  cb.addColor(BLUE);
  cb.addColor(CYAN);
  cb.addColor(MAGENTA);
  cb.addColor(YELLOW);
  cb.addColor(DARK_RED);
  cb.addColor(DARK_GREEN);
  cb.addColor(DARK_BLUE);
  cb.addColor(DARK_CYAN);
  cb.addColor(DARK_MAGENTA);
  cb.addColor(DARK_YELLOW);
  cb.addColor(WHITE);
  cb.addColor(LIGHT_GREY);
  cb.addColor(GREY);
  cb.addColor(DARK_GREY);
  cb.addColor(BLACK);

//connect Colorbar
  connect(&cb, SIGNAL(LMBColourChanged(const QColor &)),
	  man, SLOT(setLMBcolour(const QColor &)));
}


void KPaint::fileOpen()
{
  kdDebug(3000) << "fileOpen()" << endl;

  KURL url = KFileDialog::getOpenURL(QString::null, KImageIO::pattern(KImageIO::Reading), this);

  if (url.isEmpty())
    return;
  
  loadFile(url);
}


// Initiates fetch of remote file
bool KPaint::loadFile(const KURL& file)
{
  QString filename_;
  if (!KIO::NetAccess::download(file, filename_))
    return false;

  QString size;
  QString fmt;
  
  // Check the file exists and is in a known format
  fmt = KImageIO::type(filename);

  if (!fmt.isNull())
    {
      if (c->load(filename_)) {
	filename = filename_;
	modified = false;
	format = KImageIO::type(filename_);
	url = file;
	statusbar->changeItem(url.path().data(), ID_FILE_NAME);
	canvasSizeChanged();
      }
      else {
	KMessageBox::error(0,i18n("Could not open file\n"
				  "KPaint could not open the specified file."),
			   i18n("Could not open file"));
	return false;
      }
    }
  else {
    KMessageBox::error(0,
		       i18n("Unknown Format\n"
			    "KPaint does not understand the format of the "
			    "specified file\nor the file is corrupt."),
		       i18n("Unknown Format"));
    return false;
  }

  return true;
}


void KPaint::fileSave()
{
  if (!saveFile(filename, format, url))
    return;
  
  modified = false;
}


bool KPaint::saveFile(QString filename_, QString format_, KURL &url_)
{
  if (!c->save(filename_, format_))
    return false;
  filename = filename_;
  format = format_;

  if (!url_.isEmpty())
    {
      if (!KIO::NetAccess::upload(filename_, url_))
	return false;
      url = url_;
    }

  statusbar->changeItem(filename, ID_FILE_NAME);
}


void KPaint::fileSaveAs()
{
  kdDebug(3000) << "fileSaveAsCommand" << endl;

  KURL file = KFileDialog::getSaveURL(QString::null, KImageIO::pattern(KImageIO::Writing), this);
  
  if(file.isEmpty())
    return;

  // delete tempfile of not locally loaded file
  if (!url.isEmpty())
  {
    if( !url.isLocalFile() )
    {
      kdDebug(3000) << "KPaint: Deleting temp file \'" << filename.data() << "\'" << endl;

      KIO::NetAccess::removeTempFile(filename);
    }
  }

  QString tmpfile;
  if (file.isLocalFile())
    tmpfile = file.path();
  else
    tmpfile = tmpnam(0);

  if (saveFile(tmpfile, KImageIO::type(file.path()), file))
    modified = false;
}


void KPaint::fileNew()
{
  int w, h;
  QPixmap p;
  QString proto;
  canvasSizeDialog sz(0, "canvassizedlg");

  kdDebug(3000) << "File New" << endl;

  if (sz.exec()) {
    w= sz.getWidth();
    h= sz.getHeight();
    p.resize(w, h);
    p.fill(QColor("white"));
    c->setPixmap(&p);
    man->setCurrentTool(0);
    format = KImageIO::suffix(KImageIO::types(KImageIO::Writing).first());
    filename = i18n("untitled") + "." + format;
    url=KURL();

    statusbar->changeItem(filename.data(), ID_FILE_NAME);

    canvasSizeChanged();

    repaint(0);
  }
}


void KPaint::fileExit()
{
  kdDebug(3000) << "fileExit()" << endl;

  if (exit()) {
    kapp->exit(0);
  }
}

void KPaint::newWindow()
{
   kdDebug(3000) << "newWindow()" << endl;
   KPaint *kp;
   
   kp= new KPaint();
   kp->show();
}

void KPaint::closeWindow()
{
  kdDebug(3000) << "closeWindow()" << endl;
  if (exit())
    close();
}


// Edit
void KPaint::editCopy()
{
  kdDebug(3000) << "editCopy()\n" << endl;
  c->copy();
}

void KPaint::editCut()
{
  kdDebug(3000) << "editCut()\n" << endl;
  c->cut();
}

void KPaint::editPaste()
{
  kdDebug(3000) << "editPaste()\n" << endl;
  c->paste();
}

void KPaint::editPasteImage()
{
  kdDebug(3000) << "editPasteImage()" << endl;
  KPaint *kp;
  QPixmap *p;

#if 0
  if ((myapp->clipboard_) != 0) {
    p= new QPixmap(*(myapp->clipboard_));
    CHECK_PTR(p);
    kp= new KPaint();
    kp->setPixmap(p);
    kp->show();
  }
#endif
}

void KPaint::editZoomIn()
{
  kdDebug(3000) << "editZoomIn()" << endl;
  if (zoom >= 100) {
    zoom += 100;
    if (zoom > 1000)
      zoom= 1000;
  }
  else { // if (zoom < 100)
    zoom += 10;
  }
  c->setZoom(zoom);

  QString zoomstr;
  //  char *s;

  zoomstr.setNum(zoom);
  zoomstr += '%';
  //  zoomstr.append("%");
  //  s= strdup(zoomstr);

  statusbar->changeItem((const char *)zoomstr, ID_ZOOM_FACTOR);

  //  free(s);
}

void KPaint::editZoomOut()
{
  kdDebug(3000) << "editZoomOut()" << endl;
  if (zoom > 100) {
    zoom -= 100;
  }
  else { // if (zoom <= 100)
    zoom -= 10;
    if (zoom == 0)
      zoom= 10;
  }
  c->setZoom(zoom);

  QString zoomstr;
  //  char *s;

  zoomstr.setNum(zoom);
  zoomstr += '%';
  //  zoomstr.append("%");
  //  s= strdup(zoomstr);

  statusbar->changeItem((const char *)zoomstr, ID_ZOOM_FACTOR);

  //  free(s);
}

void KPaint::editMask()
{
    kdDebug(3000) << "editMask()" << endl;
}

void KPaint::editOptions()
{
    kdDebug(3000) << "editOptions()" << endl;
/* obsolet (jha)
    KKeyDialog::configureKeys(keys); */
}
  
// Image
void KPaint::imageInfo()
{
  imageInfoDialog info(c, 0, "Image Information");
  kdDebug(3000) << "imageInfo()" << endl;
  info.exec();
}

void KPaint::imageResize()
{
  kdDebug(3000) << "imageResize()" << endl;
  canvasSizeDialog sz(this);
  if (sz.exec()) {
    int w= sz.getWidth();
    int h= sz.getHeight();
    c->resizeImage(w, h);
    canvasSizeChanged();
  }
}

void KPaint::imageEditPalette()
{
    kdDebug(3000) << "imageEditPalette()" << endl;
    paletteDialog pal(c->pixmap());

    if (pal.exec()) {
      c->setPixmap(pal.pixmap());
      c->repaint(0);
    }
}

void KPaint::imageChangeDepth()
{
  QString depthstr;
  KStatusBar *sb = statusBar();
  depthDialog d(c);

  kdDebug(3000) << "imageChangeDepth()" << endl;
  if (d.exec()) {
    switch (d.depthBox->currentItem()) {
    case ID_COLOR_1:
      kdDebug(3000) << "setDepth to 1" << endl;
      c->setDepth(1);
      depthstr.sprintf(" %d bpp", 1);
      sb->changeItem(depthstr, ID_COLOR_DEPTH);
      allowEditPalette= false;
      break;
    case ID_COLOR_4:
      kdDebug(3000) << "setDepth to 4" << endl;
      c->setDepth(4);
      depthstr.sprintf(" %d bpp", 4);
      sb->changeItem(depthstr, ID_COLOR_DEPTH);
      allowEditPalette= false;
      break;
    case ID_COLOR_8:
      kdDebug(3000) << "setDepth to 8" << endl;
      c->setDepth(8);
      depthstr.sprintf(" %d bpp", 8);
      sb->changeItem(depthstr, ID_COLOR_DEPTH);
      allowEditPalette= true;
      break;
    case ID_COLOR_15:
      kdDebug(3000) << "setDepth to 15" << endl;
      c->setDepth(15);
      depthstr.sprintf(" %d bpp", 15);
      sb->changeItem(depthstr, ID_COLOR_DEPTH);
      allowEditPalette= false;
      break;
    case ID_COLOR_16:
      kdDebug(3000) << "setDepth to 16" << endl;
      c->setDepth(16);
      depthstr.sprintf(" %d bpp", 16);
      sb->changeItem(depthstr, ID_COLOR_DEPTH);
      allowEditPalette= false;
      break;
    case ID_COLOR_24:
      kdDebug(3000) << "setDepth to 24" << endl;
      c->setDepth(24);
      depthstr.sprintf(" %d bpp", 24);
      sb->changeItem(depthstr, ID_COLOR_DEPTH);
      allowEditPalette= false;
      break;
    case ID_COLOR_32:
      kdDebug(3000) << "setDepth to 32" << endl;
      c->setDepth(32);
      depthstr.sprintf("%d bpp", 32);
      sb->changeItem(depthstr, ID_COLOR_DEPTH);
      allowEditPalette= false;
      break;
    default:
      break;
    }
    // This is broken
    //    updateCommands();
  }
}

// Tool
void KPaint::setTool(int t)
{
  man->setCurrentTool(t);
}

void KPaint::toolProperties()
{
  kdDebug(3000) << "toolProperties()" << endl;
  man->showPropertiesDialog();
}


void KPaint::dummy()
{
  // just here to make KAction happy
}

#include "kpaint.moc"
