   /*

    $Id: kfax.cpp,v 1.79.2.2 2003/05/15 11:28:19 waba Exp $

    Copyright (C) 1997 Bernd Johannes Wuebben
                       wuebben@math.cornell.edu

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    */

#ifdef KDE_USE_FINAL
/* NewImage() in viewfax.cpp needs to fiddle with the Display structure */
#define XLIB_ILLEGAL_ACCESS
#endif

#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>

#include <qfile.h>
#include <qstrlist.h>
#include <qtimer.h>

#include <klocale.h>
#include <kaboutdata.h>
#include <kstandarddirs.h>
#include <kiconloader.h>
#include <kfiledialog.h>
#include <kstdaccel.h>
#include <kconfig.h>
#include <kmenubar.h>
#include <kmessagebox.h>
#include <kcmdlineargs.h>
#include <kio/netaccess.h>
#include <knotifyclient.h>
#include <ktempfile.h>
#include <kstdaction.h>
#include <kdebug.h>
#include <kurldrag.h>

#include "kfax.h"
#include "kfax.moc"
#include "version.h"
#include "viewfax.h"

TopLevel *toplevel;

extern  int GetImage(struct pagenode *pn);

void 	TurnFollowing(int How, struct pagenode *pn);
void 	handle_X_event(XEvent event);
int 	notetiff(const char *name);
void 	ShowLoop(void);
void 	SetupDisplay();
void 	mysighandler(int sig);
void    setFaxDefaults();

void 	parse(char* buf, char** args);
int     copyfile(const char* to,char* from);

extern  "C"{
int    fax2psmain(const char* faxtiff_file,FILE* psoutput, float width,float height,int scale);
int    fax2tiffmain(const char* inputfile, const char* outputfile,int bitorder,int stretchit,int type);
}

extern void g31expand(struct pagenode *pn, drawfunc df);
extern void g32expand(struct pagenode *pn, drawfunc df);
extern void g4expand(struct pagenode *pn, drawfunc df);

#define PATIENCE 100000


#undef 	min
#undef 	max
#define min(a,b)	((a)<(b)?(a):(b))
#define max(a,b)	((a)>(b)?(a):(b))

int 	ExpectConfNotify = 1;

GC 	PaintGC;
Cursor 	WorkCursor;
Cursor 	ReadyCursor;
Cursor 	MoveCursor;
Cursor 	LRCursor;
Cursor 	UDCursor;

extern Time Lasttime;
extern bool have_cmd_opt;
extern  struct pagenode *viewpage;

extern 	XImage *FlipImage(XImage *xi);
extern 	XImage *MirrorImage(XImage *xi);
extern 	XImage *RotImage(XImage *Image);
extern 	XImage *ZoomImage(XImage *Big);
extern 	void FreeImage(XImage *Image);

extern 	XImage *Image, *Images[MAXZOOM];

int 	xpos= 0, ox= 0;	/* x, old x, offset x, new xpos*/
int 	ypos= 0, oy = 0;	/* y, old y, offset y, new y */
int 	offx = 0;
int 	offy = 0;
int 	nx = 0;
int 	ny = 0;

int oz, Resize = 0, Refresh = 0;	/* old zoom, window size changed,
					   needs updating */
int PaneWidth, PaneHeight;		/* current size of our window */
int AbsX, AbsY;				/* absolute position of centre of window */

Display* qtdisplay;

int 	startingup;
Window  qtwin;    // the qt window
Window 	Win;
int 	qwindow_height;
int 	qwindow_width;
bool 	have_no_fax = TRUE;
bool	display_is_setup = FALSE;
struct 	optionsinfo fop;   // contains the fax options

extern 	struct pagenode *firstpage, *lastpage, *thispage;
extern  struct pagenode* auxpage;

extern 	struct pagenode defaultpage;

bool buttondown;

bool MyApp::x11EventFilter( XEvent * ev)
{
  if (KApplication::x11EventFilter(ev))
    return TRUE;

  if (ev->type  ==  ButtonRelease){
    /* this is so that the cursor goes back to normal on leaving the fax window
       and that the fax won't be moved when I reenter after I release the mouse*/

    if (buttondown == true){
      buttondown = false;
      XDefineCursor(qtdisplay, Win, ReadyCursor);
      XFlush(qtdisplay);
    }
  }
  if ( ev->xany.window == qtwin ||
       ev->xany.window == Win){

    if(startingup || have_no_fax)
      return FALSE;

    toplevel->handle_X_event(*ev);
    ev->xany.window = qtwin;

  }

  return FALSE;

}

TopLevel::TopLevel (QWidget *, const char *name)
    : KMainWindow (0, name)
{
  setMinimumSize (100, 100);

  printdialog = NULL;
  buttondown = false;

  setupMenuBar();
  setupStatusBar();
  setStandardToolBarMenuEnabled( true );

  updateActions();

  createGUI();

  readSettings();

  faxqtwin = new QFrame(this);

  qtwin = faxqtwin->winId();
  faxqtwin->setFrameStyle(QFrame::Panel | QFrame::Sunken);

  // Create a Vertical scroll bar

  vsb = new QScrollBar( QScrollBar::Vertical,faxqtwin,"scrollBar" );
  vsb->hide();
  connect( vsb, SIGNAL(valueChanged(int)), SLOT(scrollVert(int)) );

  // Create a Horizontal scroll bar

  hsb = new QScrollBar( QScrollBar::Horizontal,faxqtwin,"scrollBar" );
  connect( hsb, SIGNAL(valueChanged(int)), SLOT(scrollHorz(int)) );
  hsb->hide();


  setCentralWidget(faxqtwin);

  setAcceptDrops(true);

  resize(editor_width,editor_height);
  show();
}


TopLevel::~TopLevel()
{
}

void TopLevel::setupMenuBar()
{
  // File menu
  KStdAction::open( this, SLOT( faxOpen() ), actionCollection() );
  actRecent =  KStdAction::openRecent( this, SLOT( faxOpen( const KURL & ) ), 
          actionCollection() );
  actSave = KStdAction::save( this, SLOT( faxSave() ), actionCollection() );
  actSaveAs = KStdAction::saveAs( this, SLOT( faxSaveAs() ), 
          actionCollection() );
  actPrint = KStdAction::print( this, SLOT( print() ), actionCollection() );
  KStdAction::quit( this, SLOT( close() ), actionCollection() );

  actAdd = new KAction( i18n( "&Add..." ), "filenew", KShortcut(), this, 
      SLOT( faxAdd() ), actionCollection(), "file_add_fax" );

  actRecent->setMaxItems( 5 );

  // View Menu
  actSize = KStdAction::actualSize( this, SLOT( actualSize() ), 
      actionCollection() );
  actZoomIn = KStdAction::zoomIn( this, SLOT( zoomin() ), actionCollection() );
  actZoomOut = KStdAction::zoomOut( this, SLOT( zoomout() ), 
      actionCollection() );

  actRotate = new KAction( i18n( "&Rotate Page" ), "rotate", KShortcut(), this,
      SLOT( rotatePage() ), actionCollection(), "view_rotate" );
  actMirror = new KAction( i18n( "Mirror Page" ), KShortcut(), this,
      SLOT( mirrorPage() ), actionCollection(), "view_mirror" );
  actFlip = new KAction( i18n( "&Flip Page" ), KShortcut(), this,
      SLOT( flipPage() ), actionCollection(), "view_flip" );

  // Go menu
  actNext = KStdAction::next( this, SLOT( nextPage() ), actionCollection() );
  actPrev = KStdAction::prior( this, SLOT( prevPage() ), actionCollection() );
  actFirst = KStdAction::firstPage( this, SLOT( firstPage() ), 
      actionCollection() );
  actLast = KStdAction::lastPage( this, SLOT( lastPage() ), 
      actionCollection() );

  // Settings menu
  actStatus = KStdAction::showStatusbar( this, SLOT( toggleStatusBar() ),
      actionCollection() );
  KStdAction::preferences( this, SLOT( faxoptions() ), actionCollection() );
}

void TopLevel::setupStatusBar()
{
  statusbar = statusBar();

  statusbar->insertFixedItem(i18n("w: 00000 h: 00000"), ID_INS_OVR);
  statusbar->insertFixedItem(i18n("Res: XXXXX"), ID_GENERAL);
  statusbar->insertFixedItem(i18n("Type: XXXXXXX"), ID_TYPE);
  statusbar->insertFixedItem(i18n("Page: XX of XX"), ID_PAGE_NO);
  statusbar->insertItem("", ID_FNAME, 1);
  statusbar->setItemAlignment( ID_FNAME, AlignLeft );

  statusbar->changeItem("", ID_INS_OVR);
  statusbar->changeItem("", ID_GENERAL);
  statusbar->changeItem("", ID_TYPE);
  statusbar->changeItem("", ID_PAGE_NO);
  statusbar->changeItem("", ID_FNAME);
}

void TopLevel::readSettings()
{
  config = kapp->config();

  applyMainWindowSettings( config, "MainWindowSettings" );

  actRecent->loadEntries( config );

  config->setGroup("General Options");

  lastDir = config->readEntry("DefaultPath", QDir::currentDirPath());
  editor_width = config->readNumEntry("Width", 550);
  editor_height = config->readNumEntry("Height", 400);

  config->setGroup("Fax Options");

  fop.width = config->readNumEntry("width", 1728);
  fop.resauto = config->readNumEntry("resauto", 1);
  fop.geomauto = config->readNumEntry("geomauto", 1);
  fop.height = config->readNumEntry("height", 2339);
  fop.fine = config->readNumEntry("resolution", 1);
  fop.flip = config->readNumEntry("flip", 0);
  fop.invert = config->readNumEntry("invert", 0);
  fop.lsbfirst = config->readNumEntry("lsb", 0);
  fop.raw = config->readNumEntry("raw", 3);

  setFaxDefaults();

  config->setGroup("Printing");

  pi.cmd = config->readEntry("PrintCommand", "lpr");
  pi.file = config->readEntry("PrintFile");
  pi.lpr = config->readNumEntry("print", 1);
  pi.scale = config->readNumEntry("scale", 0);
  pi.margins = config->readNumEntry("margins", 0);
  pi.xmargin = config->readDoubleNumEntry("xmargin", 0.0);
  pi.ymargin = config->readDoubleNumEntry("ymargin", 0.0);
  pi.pagesize = config->readEntry("PageSize",US_LETTER);
}

void TopLevel::updateActions()
{
  actAdd->setEnabled( thispage );
  actSave->setEnabled( thispage );
  actSaveAs->setEnabled( thispage );
  actPrint->setEnabled( thispage );

  actRotate->setEnabled( thispage );
  actFlip->setEnabled( thispage );
  actMirror->setEnabled( thispage );

  updateGoActions();
  updateZoomActions();
}

void TopLevel::updateGoActions()
{
  actNext->setEnabled( thispage && thispage->next );
  actPrev->setEnabled( thispage && thispage->prev );
  actFirst->setEnabled( thispage && thispage->prev );
  actLast->setEnabled( thispage && thispage->next );
}

void TopLevel::updateZoomActions()
{
  actSize->setEnabled( Image && oz > 0 );
  actZoomIn->setEnabled( Image && oz > 0 );
  actZoomOut->setEnabled( Image && oz < MAXZOOM && Image->width >= 256 );
}

bool TopLevel::queryClose()
{
  saveMainWindowSettings( config, "MainWindowSettings" );
  actRecent->saveEntries( config );

  return true;
}

void TopLevel::writeSettings()
{
  config = kapp->config();
	
  config->setGroup("General Options");

  config->writeEntry("DefaultPath", lastDir);
  config->writeEntry("Width", this->width());
  config->writeEntry("Height", this->height());

  config->setGroup("Fax Options");

  config->writeEntry("resauto",fop.resauto);
  config->writeEntry("geomauto",fop.geomauto);
  config->writeEntry("width",fop.width);
  config->writeEntry("height",fop.height);
  config->writeEntry("resolution",fop.fine);
  config->writeEntry("flip",fop.flip);
  config->writeEntry("invert",fop.invert);
  config->writeEntry("lsb",fop.lsbfirst);
  config->writeEntry("raw",fop.raw);

  config->setGroup("Printing");

  config->writeEntry("PrintCommand",pi.cmd);
  config->writeEntry("PrintFile",pi.file);
  config->writeEntry("print",pi.lpr);
  config->writeEntry("scale",pi.scale);
  config->writeEntry("PageSize",pi.pagesize);
  config->writeEntry("margins",pi.margins);
  config->writeEntry("xmargin", pi.xmargin);
  config->writeEntry("ymargin", pi.ymargin);
  config->sync();	
}

void TopLevel::faxOpen()
{
  KURL url = KFileDialog::getOpenURL(lastDir, "*");

  faxOpen( url );

  actRecent->addURL( fileURL );
}

void TopLevel::faxOpen( const KURL & url )
{
  if (!url.isValid())
    return;

  faxClose();
  faxAdd( url );

  fileURL = url;

  updateActions();
}

void TopLevel::faxAdd()
{
  KURL url = KFileDialog::getOpenURL(lastDir, "*");

  faxAdd( url );

  actRecent->addURL( fileURL );
}

void TopLevel::faxAdd( const KURL & url )
{
  if (!url.isValid())
    return;

  if( url.isLocalFile() )
  {
    lastDir = url.directory();
  }

  openNetFile(url);  

  updateGoActions();
}

void TopLevel::faxSave()
{
  saveNetFile(fileURL);  
}

void TopLevel::faxSaveAs()
{
  fileURL = KFileDialog::getOpenURL(lastDir, "*");

  if (fileURL.isEmpty())
    return;

  if( fileURL.isLocalFile() )
  {
    lastDir =  fileURL.directory();
  }

  faxSave();

  actRecent->addURL( fileURL );
}

void TopLevel::zoom( int factor )
{
  if(!thispage || !Image || !faxqtwin || !display_is_setup)
    return;

  Image = Images[ factor ];
  Resize = Refresh = 1;

  int i;

  if (Image == NULL) {
    for (i = factor; i && (Images[i] == NULL); i--)
      ;
    for (; i != factor; i++){
      Images[i+1] = ZoomImage(Images[i]);
      Image = Images[i+1];

      if(Images[i+1] == NULL){ // out of memory	
	Image = Images[i];
	break;
      }
    }
  }

  PaneWidth = Image->width;
  PaneHeight = Image->height;

  resizeView();
  putImage();

  uiUpdate();

  updateZoomActions();
}

void TopLevel::zoomin()
{
  if ( oz > 0 )
  {
    oz--;
    zoom( oz );
  }
}

void TopLevel::zoomout()
{
  if (oz < MAXZOOM && Image->width >= 256)
  {
    ++oz;
    zoom( oz );
  }
}

void TopLevel::actualSize()
{
  oz = 0;
  zoom( oz );
}

void TopLevel::openadd(QString filename)
{
  auxpage = lastpage;

  (void) notetiff(QFile::encodeName(filename));	

  if( firstpage != lastpage )
  {
    if(auxpage->next)
      auxpage = auxpage->next;
  }

  // auxpage should now point to the first pagenode which was created for
  // the newly added fax file.
  have_no_fax = false;
  thispage = auxpage;
  newPage();
  resizeView();
  putImage();
}

void TopLevel::toggleStatusBar()
{
  if( actStatus->isChecked() )
    statusbar->show();
  else
    statusbar->hide();

  resizeView();
}

void TopLevel::resizeEvent(QResizeEvent *e)
{
  KMainWindow::resizeEvent(e);

  resizeView();
}

void TopLevel::wheelEvent( QWheelEvent *e )
{
  e->accept();

  if ( e->state() & ShiftButton )
  {
    if ( e->delta() < 0 )
      zoomin();
    else
      zoomout();
  }
  else
  {
    int offset = QApplication::wheelScrollLines()*vsb->lineStep();
    if ( e->state() & ControlButton )
      offset = vsb->pageStep();
    offset = -e->delta()*offset/120;
    vsb->setValue( vsb->value() + offset );
  }
}

void TopLevel::resizeView()
{
  if(!faxqtwin || !display_is_setup)
    return;

//printf("In resizeView() entered\n");

  qwindow_width = faxqtwin->width();
  qwindow_height = faxqtwin->height();

  if( hsb->isVisible())
    qwindow_height -= 16;

  if( vsb->isVisible())
    qwindow_width  -= 16;

  if(Image){
    PaneWidth = Image->width;
    PaneHeight = Image->height;
  }

  //  printf("faxw %d qtw %d\n", PaneWidth , faxqtwin->width());

  if( (PaneHeight  > qwindow_height ) &&
      (PaneWidth  > qwindow_width)){

    vsb->setGeometry(faxqtwin->width() - 16,0,16,faxqtwin->height()-16);
    hsb->setGeometry(0,faxqtwin->height() - 16 ,faxqtwin->width()-16,16);

    qwindow_width = faxqtwin->width() -16;
    qwindow_height = faxqtwin->height()-16;


    vsb->raise();
    vsb->show();
    hsb->show();
  }
  else{

    if( PaneHeight  > qwindow_height){
      vsb->setGeometry(faxqtwin->width() - 16,0,16,faxqtwin->height());


      qwindow_width = faxqtwin->width() -16 ;
      qwindow_height = faxqtwin->height();


      vsb->show();
      hsb->hide();
      hsb->raise();
    }
     else
       vsb->hide();

    if( PaneWidth  > qwindow_width ){
      hsb->setGeometry(0,faxqtwin->height() - 16 ,faxqtwin->width(),16);
      hsb->show();
      hsb->raise();
      vsb->hide();
      qwindow_width = faxqtwin->width() ;
      qwindow_height = faxqtwin->height() -16;

    }
    else
      hsb->hide();

  }

  if(Image){
    hsb->setRange(0,max(0,Image->width - qwindow_width));
    hsb->setSteps(5,qwindow_width/2);
    //    printf("hsb range: %d\n",max(0,Image->width - qwindow_width));
    vsb->setRange(0,max(0,Image->height - qwindow_height));
    vsb->setSteps(5,qwindow_height/2);
    //    printf("vsb range: %d\n",max(0,Image->height - qwindow_height));
    //    printf("vsb min %d vdb max %d\n",vsb->minValue(),vsb->maxValue());
  }


  Resize = 1;
  uiUpdate();

}

void TopLevel::print(){
  if(!thispage){
    KMessageBox::sorry(this, i18n("There is no document active."));
    return;
  }

  if(!printdialog){
    printdialog = new PrintDialog(NULL,"print",true);
    connect(printdialog,SIGNAL(print()),this,SLOT(printIt()));

  }

  struct printinfo newpi;

  newpi.file = pi.file;
  newpi.cmd = pi.cmd;
  newpi.lpr = pi.lpr;
  newpi.scale = pi.scale;
  newpi.pagesize = pi.pagesize;
  newpi.margins = pi.margins;
  newpi.xmargin = pi.xmargin;
  newpi.ymargin = pi.ymargin;

  printdialog->setWidgets(&newpi);

  QPoint point = this->mapToGlobal (QPoint (0,0));

  QRect pos = this->geometry();
  printdialog->setGeometry(point.x() + pos.width()/2  - printdialog->width()/2,
			   point.y() + pos.height()/2 - printdialog->height()/2,
			   printdialog->width(),
			   printdialog->height()
			   );

  printdialog->show();

}

void TopLevel::printIt(){

  if(!firstpage)
    return;

  if(!printdialog)
    return;

  kapp->processEvents();

  struct printinfo *newpi;

  newpi = printdialog->getInfo();

  pi.file = newpi->file;
  pi.cmd = newpi->cmd;
  pi.lpr = newpi->lpr;
  pi.scale = newpi->scale;
  pi.pagesize = newpi->pagesize;
  pi.margins = newpi->margins;
  pi.xmargin = newpi->xmargin;
  pi.ymargin = newpi->ymargin;


  float width = 8.5;
  float height = 11;

  if(pi.pagesize ==  US_LETTER){
    width = 8.5;
    height = 11;
  }

  if(pi.pagesize == US_LEGAL){
    width = 8.5;
    height = 14;
  }

  if(pi.pagesize == US_LEDGER){
    width = 11;
    height = 17;
  }

  if(pi.pagesize == US_EXECUTIVE){
    width = 7.25;
    height = 10.5;
  }

  if(pi.pagesize == DIN_A3){
    width = 12.69;
    height = 16.53;
  }

  if(pi.pagesize == DIN_A4){
    width = 8.267;
    height = 11.692;
  }

  if(pi.pagesize == DIN_A5){
    width = 5.944;
    height = 8.2675;
  }

  if(pi.pagesize == DIN_A6){
    width = 4.2125;
    height = 5.8258;
  }

  if(pi.pagesize == DIN_B4){
    width = 10.04;
    height = 14.33;
  }

  if(pi.pagesize == JAP_LETTER){
    width = 7.165;
    height = 10.1175;
  }

  if(pi.pagesize == JAP_LEGAL){
    width = 10.1175;
    height = 14.33;
  }


  if(pi.margins == 1){
    width  = width - pi.xmargin/2.54*2.0; /* 2.0 since we want this margin both at the
					     top as well as on the bottom */
    height = height - pi.ymargin/2.54*2.0;
  }

  if(width <= 0.0 || height <=0.0 ){

    QString str = i18n("Invalid page dimensions:\nWidth %1 Height %2\n")
		.arg(width, 2).arg(height, 2);
    KMessageBox::sorry(this, str);

    return;
  }

  FILE* psfile;

  if(pi.lpr){

    psfile = NULL;
    psfile = popen(QFile::encodeName(pi.cmd),"w");

    if(psfile == NULL){
      QString str = i18n("Cannot print to \"%1\"\n").arg(pi.cmd);
      KMessageBox::sorry(this, str);
      return;
    }

  }
  else{

    psfile = fopen(QFile::encodeName(pi.file),"w");
    if(psfile == NULL){
      QString str = i18n("Could not create %1\n").arg(pi.file);
      KMessageBox::error(this, str);
      return;
    }
  }

  QApplication::setOverrideCursor( waitCursor );
  XDefineCursor(qtdisplay, Win, WorkCursor);
  kapp->processEvents();

  struct pagenode *pn;
  QStrList filelist;

  for(pn = firstpage; pn; pn = pn->next){

    if(filelist.find(pn->pathname) != -1) // we have printed this fax file already
      continue;

    filelist.append(pn->pathname);

    if(pn->type == FAX_TIFF){

      fax2psmain(pn->pathname,psfile,width,height,pi.scale);

    }

    if(pn->type == FAX_RAW){

      int faxtype = 31;

      if(defaultpage.expander ==  g32expand)
	faxtype = 32;

      if(defaultpage.expander ==  g4expand)
	faxtype = 4;

      if(defaultpage.expander ==  g31expand)
	faxtype = 31;

      KTempFile tmpFile;
      tmpFile.setAutoDelete(true);

      fax2tiffmain(pn->pathname,QFile::encodeName(tmpFile.name()),pn->lsbfirst,pn->vres?0:1,faxtype);
      fax2psmain(QFile::encodeName(tmpFile.name()),psfile,width,height,pi.scale);
    }

  }

  if(pi.lpr)
    pclose(psfile);
  else
    fclose(psfile);

  QApplication::restoreOverrideCursor();
  XDefineCursor(qtdisplay, Win, ReadyCursor);

}

void TopLevel::saveNetFile( const KURL& u)
{
    if ( u.isMalformed() )
    {
	KMessageBox::sorry(this, i18n("Malformed URL"));
	return;
    }

    // Just a usual file ?
    if ( u.isLocalFile() )
    {
      statusbar->message( i18n( "Saving..." ) );
      int res = copyfile(QFile::encodeName(u.path()),thispage->pathname);
      statusbar->clear();
      if (res==0) {
	  KMessageBox::error(this, i18n("Failure in 'copy file()'\n"
				"Could not save file!"));
	  return;
	}
	return;
      }

    KTempFile tmpFile;
    tmpFile.setAutoDelete(true);

    int res = copyfile(QFile::encodeName(tmpFile.name()),thispage->pathname );
    if (res==0) {
      KMessageBox::error(this, i18n("Failure in 'copy file()'\n"
			    "Could not save file!"));
      return;
    }

    KIO::NetAccess::upload( tmpFile.name(), u );
}

void TopLevel::openNetFile( const KURL &u)
{
  if ( u.isMalformed() )
  {
	KMessageBox::error(this, i18n("Malformed URL"));
	return;
  }

  if ( u.isLocalFile() )
  {
    QString string = i18n("Loading '%1'").arg(u.path());
    statusbar->message(string);
    openadd( u.path());
    statusbar->clear();
  }
  else
  {
    statusbar->message(i18n("Downloading..."));
    QString tmpFile = QString::null;
    if ( KIO::NetAccess::download( u, tmpFile ) )
    {
      openadd( tmpFile );
      setCaption( u.prettyURL() );
    }
    statusbar->clear();
    KIO::NetAccess::removeTempFile( tmpFile );
  }
}

void TopLevel::dragEnterEvent( QDragEnterEvent * event)
{
  event->accept(KURLDrag::canDecode(event));
}

void TopLevel::dropEvent( QDropEvent * event)
{

  KURL::List list;
  
  if (KURLDrag::decode(event, list) && !list.isEmpty())
  {
    // Load the first file in this window
    const KURL &url = list.first();
    openNetFile( url );
  }
}

void SetupDisplay(){

  if(display_is_setup){
    return;
  }

  display_is_setup = TRUE;

  xpos = ypos = ox = oy = 0;
  ExpectConfNotify = 1;

  /*  XSizeHints size_hints;*/

  Win = XCreateSimpleWindow(qtdisplay,qtwin,1,1,
			    1,1,
			    0,
			    BlackPixel(qtdisplay,XDefaultScreen(qtdisplay)),
			    WhitePixel(qtdisplay,XDefaultScreen(qtdisplay)));

  PaintGC = XCreateGC(qtdisplay, Win, 0L, (XGCValues *) NULL);
  XSetForeground(qtdisplay, PaintGC, BlackPixel(qtdisplay, XDefaultScreen(qtdisplay) ));
  XSetBackground(qtdisplay, PaintGC, WhitePixel(qtdisplay, XDefaultScreen(qtdisplay) ));
  XSetFunction(qtdisplay, PaintGC, GXcopy);
  WorkCursor = XCreateFontCursor(qtdisplay, XC_watch);
  //ReadyCursor = XCreateFontCursor(qtdisplay, XC_plus);
  ReadyCursor = XCreateFontCursor(qtdisplay, XC_hand2);
  MoveCursor = XCreateFontCursor(qtdisplay, XC_fleur);
  LRCursor = XCreateFontCursor(qtdisplay, XC_sb_h_double_arrow);
  UDCursor = XCreateFontCursor(qtdisplay, XC_sb_v_double_arrow);

  XSelectInput(qtdisplay, Win, Button2MotionMask | ButtonPressMask |
	       ButtonReleaseMask | ExposureMask | KeyPressMask |
	       SubstructureNotifyMask | LeaveWindowMask | OwnerGrabButtonMask |
	       StructureNotifyMask);

  XMapRaised(qtdisplay, Win);

  XDefineCursor(qtdisplay, Win, ReadyCursor);
  XFlush(qtdisplay);

  for (oz = 0; oz < MAXZOOM; oz++)
    Images[oz] = NULL;

  // Start at half the Size
  oz = 1;
}

void TopLevel::handle_X_event(XEvent Event)
{
  if(!thispage || !Image || !faxqtwin || !display_is_setup)
    return;

  bool putimage = false; // Do we actually have to write the image to the scree?

  do {
    switch(Event.type) {
    case MappingNotify:
      XRefreshKeyboardMapping((XMappingEvent *)(&Event));
		break;

    case LeaveNotify:
      /*      buttondown = false;
	XDefineCursor(qtdisplay, Win, ReadyCursor);
	XFlush(qtdisplay);*/
      break;
    case Expose:
      {

	if(Event.xexpose.count != 0)
	  break;
	
	if(!Image)
	  break;

	putimage = TRUE;
      }	
    break;

    case KeyPress:
      if (ExpectConfNotify &&
	  (Event.xkey.time < (Lasttime + PATIENCE)))
	break;
      Lasttime = Event.xkey.time;
      ExpectConfNotify = 0;
      switch(XKeycodeToKeysym(qtdisplay, Event.xkey.keycode, 0)) {
      case XK_m:
	mirrorPage();
	if (Event.xkey.state & ShiftMask)
	  TurnFollowing(TURN_M, thispage->next);
  	break;
      case XK_o:
	zoomout();
	break;

      case XK_i:
	zoomin();
	break;

      case XK_Up:
	ypos-= qwindow_height / 3;
	putimage = TRUE;
	break;
      case XK_Down:
	ypos+= qwindow_height / 3;
	putimage = TRUE;
	break;
      case XK_Left:
	xpos-= qwindow_width / 4;
	putimage = TRUE;
	break;
      case XK_Right:
	xpos+= qwindow_width / 4;
	putimage = TRUE;
	break;
      case XK_Home:
      case XK_R7:	
	if (Event.xkey.state & ShiftMask) {
	  thispage = firstpage;
	  newPage();
	  resizeView();
	  putImage();
	  break;
	}
	xpos= 0;
	ypos= 0;
	putImage();
	break;
      case XK_End:
      case XK_R13:
	if (Event.xkey.state & ShiftMask) {
	  thispage = lastpage;
	  newPage();
	  resizeView();
	  putImage();
	  break;
	}
	xpos= Image->width;
	ypos= Image->height;
	putImage();
	break;
      case XK_l:
	rotatePage();
	if (Event.xkey.state & ShiftMask)
	  TurnFollowing(TURN_L, thispage->next);
	break;
      case XK_p:
      case XK_minus:
      case XK_Prior:
      case XK_R9:
      case XK_BackSpace:
	prevPage();
	break;
      case XK_n:
      case XK_plus:
      case XK_space:
      case XK_Next:
      case XK_R15:
	nextPage();
	break;
      case XK_u:
	flipPage();
	if (Event.xkey.state & ShiftMask)
	  TurnFollowing(TURN_U, thispage->next);
	break;

      case XK_q:
	if (viewpage) {
	  thispage = viewpage;
	  viewpage = NULL;
	  newPage();
	  resizeView();
	  putImage();
	}

      }

      break;

    case ButtonPress:

      if (ExpectConfNotify && (Event.xbutton.time < (Lasttime + PATIENCE)))
	break;

      Lasttime = Event.xbutton.time;
      ExpectConfNotify = 0;


      switch (Event.xbutton.button) {

      case Button1:
	buttondown = true;

	switch (((Image->width > qwindow_width)<<1) |
		(Image->height > qwindow_height)) {
	case 0:
	  break;
	case 1:
	  XDefineCursor(qtdisplay, Win, UDCursor);
	  break;
	case 2:
	  XDefineCursor(qtdisplay, Win, LRCursor);
	  break;
	case 3:
	  XDefineCursor(qtdisplay, Win, MoveCursor);
	}

	XFlush(qtdisplay);
		offx = Event.xbutton.x;
		offy = Event.xbutton.y;
	break;

      }

      break;

    case MotionNotify:
      if(!buttondown)
	break;
      do {

	nx = Event.xmotion.x;
	ny = Event.xmotion.y;


      } while (XCheckTypedEvent(qtdisplay, MotionNotify, &Event));


      xpos+= offx - nx;
      ypos+= offy - ny;

      offx = nx;
      offy = ny;

      putimage = TRUE;

      break;

    case ButtonRelease:

      if (Event.xbutton.button == Button1) {

	buttondown = false;
	XDefineCursor(qtdisplay, Win, ReadyCursor);
	XFlush(qtdisplay);
      }

    }

  } while (XCheckWindowEvent(qtdisplay, Win, KeyPressMask|ButtonPressMask, &Event));

  if(putimage == TRUE) {
    Refresh = Resize = 1;
    putImage();
  }
}

void TopLevel::rotatePage()
{
  if(!thispage || !Image || !faxqtwin || !display_is_setup)
    return;

  XImage *newrotimage = NULL;

  XDefineCursor(qtdisplay, Win, WorkCursor);
  XFlush(qtdisplay);

  newrotimage = RotImage(Images[0]);

  if(newrotimage == NULL){ // out of memory
    XDefineCursor(qtdisplay, Win, ReadyCursor);
    return;
  }

  thispage->extra = Image = newrotimage;
  thispage->orient ^= TURN_L;

  int i;
  for (i = 1; Images[i]; i++) {
    FreeImage(Images[i]);
    Images[i] = NULL;
  }

  Images[0] = Image;

  for (i = 1; i <= oz; i++){

    Images[i] = ZoomImage(Images[i-1]);
    Image = Images[i];
    if(Images[i] == NULL){// out of memory
      Image = Images[i -1];
      break;
    }
  }

  { int t = xpos ; xpos= ypos; ypos= t; }

  Refresh = Resize = 1;

  putImage();
}

void TopLevel::flipPage()
{
  if(!thispage || !Image || !faxqtwin || !display_is_setup)
    return;

  XImage *newflipimage = NULL;

  XDefineCursor(qtdisplay, Win, WorkCursor);
  XFlush(qtdisplay);

  newflipimage = FlipImage(Images[0]);

  if(newflipimage == NULL){ // out of memory
    XDefineCursor(qtdisplay, Win, ReadyCursor);
    return;
  }

  thispage->extra = Images[0] = newflipimage;
  thispage->orient ^= TURN_U;

  int i;
  for (i = 1; Images[i]; i++) {
    FreeImage(Images[i]);
    Images[i] = ZoomImage(Images[i-1]);
    if(Images[i]== NULL){// out of memory
      break;
    }
  }

  Image = Images[min(i-1,oz)];

  Refresh = Resize = 1;
  putImage();
}

void TopLevel::mirrorPage()
{
  if(!thispage || !Image || !faxqtwin || !display_is_setup)
    return;

  XImage *newmirror = NULL;

  XDefineCursor(qtdisplay, Win, WorkCursor);
  XFlush(qtdisplay);

  newmirror = MirrorImage(Images[0]);

  if(newmirror == NULL){ // out of memory
    XDefineCursor(qtdisplay, Win, ReadyCursor);
    return;
  }
  thispage->extra = Images[0] = newmirror;

  thispage->orient ^= TURN_M;

  int i;
  for (i = 1; Images[i]; i++) {
    FreeImage(Images[i]);
    Images[i] = ZoomImage(Images[i-1]);
    if (Images[i] == NULL) // out of memory
      break;
  }
  Image = Images[min(oz,i-1)];

  Refresh = Resize = 1;
  putImage();
}

void TopLevel::scrollHorz(int){


  if(!Image)
    return;

  //  printf("hsb value: %d\n",hsb->value());
  xpos=  hsb->value() + qwindow_width/2;

  Refresh = 1;
  putImage();

}

void TopLevel::scrollVert(int ){

  if(!Image)
    return;

  //  printf("vsb value: %d\n",vsb->value());
  ypos=  vsb->value() + qwindow_height/2;

  Refresh = 1;
  putImage();
}

void TopLevel::lastPage()
{
  if(!thispage)
    return;

  if ( thispage->next )
  {
    while(thispage->next != NULL)
      thispage = thispage->next;

    newPage();
    resizeView();
    putImage();
  }

  updateGoActions();
}

void TopLevel::firstPage()
{
  if(!thispage)
    return;

  if ( thispage->prev )
  {
    while(thispage->prev != NULL)
      thispage = thispage->prev;

    newPage();
    resizeView();
    putImage();
  }

  updateGoActions();
}
void TopLevel::nextPage()
{
  if(!thispage)
    return;

  if (thispage->next)
  {
    thispage = thispage->next;

    newPage();
    resizeView();
    putImage();
  }

  updateGoActions();
}

void TopLevel::prevPage()
{
  if(!thispage)
    return;

  if (thispage->prev)
  {
    thispage = thispage->prev;

    newPage();
    resizeView();
    putImage();
  }

  updateGoActions();
}

void TopLevel::newPage(){


  if(!display_is_setup)
    SetupDisplay();


  XDefineCursor(qtdisplay, Win, WorkCursor);
  XFlush(qtdisplay);


  int i;

  for (i = 1; Images[i]; i++) {
    FreeImage(Images[i]);
    Images[i] = NULL;
  }

  int k = -1;


  if(!thispage) {
    XDefineCursor(qtdisplay, Win, ReadyCursor);
    return;
  }

  if (Pimage(thispage) == NULL){

    while((k != 0) && (k != 3) && (k !=1))
      k = GetImage(thispage);

  }

  if (k == 3 ){

    XDefineCursor(qtdisplay, Win, ReadyCursor);
    FreeFax();
    /*    KMessageBox::sorry(i18n("Bad Fax File k=3"));*/
    return;
  }

  if (k == 0 ){

    XDefineCursor(qtdisplay, Win, ReadyCursor);
    FreeFax();
    /*    KMessageBox::sorry(i18n("Bad Fax File k=0"));*/
    return;
 }

  Image =  Images[0] = Pimage(thispage);

  setCaption(thispage->name);

  for (i = 1; i <= oz; i++){
    Images[i] = ZoomImage(Images[i-1]);
    Image = Images[i];
    if (Images[i] == NULL){ // out of memory
      Image = Images[i-1];
      break;
    }
  }

  PaneWidth = Image->width;
  PaneHeight = Image->height;
  Refresh = 1;

  XDefineCursor(qtdisplay, Win, ReadyCursor);
  uiUpdate();

}


void TopLevel::faxClose()
{
  FreeFax();

  setCaption(i18n("KFax"));
  // TODO replace this with unmapping the window.
  if(display_is_setup)
    XResizeWindow(qtdisplay,Win,1,1); // we want a clear gray background.

  resizeView();
  vsb->hide();
  hsb->hide();

  fileURL = "";

  updateActions();
}

void TopLevel::FreeFax(){


  if(display_is_setup)
    XClearWindow(qtdisplay, Win);

  for (int i = 1; Images[i]; i++) {
	  FreeImage(Images[i]);
	  Images[i] = NULL;
  }

  pagenode *pn;

  for (pn = firstpage; pn; pn = pn->next){
    if(Pimage(pn)){
       FreeImage(Pimage(pn));
    }
  }

  Image = NULL;

  for (pn = firstpage; pn; pn = pn->next){
    if(pn->pathname){
       free(pn->pathname);
    }
  }


  if(firstpage){
    for(pn = firstpage->next; pn; pn = pn->next){
      if(pn->prev){
	free(pn->prev);
      }
    }	
  }

  if(lastpage)
    free(lastpage);

  firstpage = lastpage = viewpage = thispage = auxpage = NULL;

  uiUpdate();

}

void TopLevel::uiUpdate(){

  if(thispage){

    struct pagenode *pn ;
    int pages = 0;
    int currentpage = 0;

    for(pn = firstpage; pn ; pn = pn->next){
      pages ++;
      if (thispage == pn)
	currentpage = pages;
    }

    QString pagestr = i18n("Page: %1 of %2").arg(currentpage).arg(pages);

    statusbar->changeItem(pagestr, ID_PAGE_NO);

    if(Image){
      QString wh = i18n("W: %1 H: %2").arg(Image->width).arg(Image->height);
      statusbar->changeItem(wh, ID_INS_OVR);
    }

    QString resolution = i18n("Res: %1").arg(thispage->vres?i18n("Fine"):i18n("Normal"));
    statusbar->changeItem(resolution, ID_GENERAL);

    statusbar->changeItem(thispage->name, ID_FNAME);

    QString typestring;

    if(thispage->type == FAX_TIFF){
      typestring = i18n("Type: Tiff ");
    }	
    else if ( thispage->type == FAX_RAW){
      typestring = i18n("Type: Raw ");
    }

    if ( thispage->expander == g31expand )
      typestring += "G3";

    if ( thispage->expander == g32expand )
      typestring += "G3 2D";

    if ( thispage->expander == g4expand )
      typestring += "G4";

    statusbar->changeItem(typestring,ID_TYPE);
    updateActions();
  }
}

void kfaxerror(const QString& title, const QString& error){
  KMessageBox::error(toplevel, error, title);
}

void TopLevel::putImage()
{

  // TODO do I really need to set Refresh or Resize to 1 , is there
  // really still someonce calling this with out haveing set Refresh or Resize to 1?

  if ( !Image || !display_is_setup || !thispage )
    return;

  if ( qwindow_width > Image->width){
	  xpos= Image->width/2;
  }
  else{
    if(xpos< qwindow_width/2){
      xpos = qwindow_width/2;
    }
    else{
      if(xpos> Image->width - qwindow_width/2){
	xpos= Image->width - qwindow_width/2;
      }

    }
  }

  if ( qwindow_height > Image->height){
    ypos= Image->height/2;
  }
  else{
    if(ypos< qwindow_height/2){
      ypos = qwindow_height/2;
    }
    else{
      if(ypos> Image->height - qwindow_height/2){
	ypos= Image->height - qwindow_height/2;
      }

    }
  }

  if (xpos!= ox || ypos!= oy || Refresh || Resize){

    /* In the following we use qwindow_height -1 etc since the main view
       has a sunken frame and I need to offset by 1 pixel to the right and
       one pixel down so that I don't paint my fax into the border of the frame*/

    XResizeWindow(qtdisplay,Win,min(qwindow_width -1,Image->width ),
		  min(qwindow_height -1,Image->height ));

    XPutImage(qtdisplay, Win, PaintGC, Image,
	      max(xpos - qwindow_width/2,0), max(ypos - qwindow_height/2,0),
	      0, 0, min(qwindow_width -1,Image->width)  ,
	      min(qwindow_height -1,Image->height) );

    vsb->setValue(max(ypos - qwindow_height/2,0));
    hsb->setValue(max(xpos - qwindow_width/2,0));

    XFlush(qtdisplay);
  }

  ox = xpos;
  oy = ypos;

  Resize = Refresh = 0;

}

void TopLevel::faxoptions(){

 OptionsDialog * opd = new OptionsDialog(this, "options");
 opd->setWidgets(&fop);

 if(opd->exec()){

   struct optionsinfo *newops;
   newops = opd->getInfo();

   fop.resauto 	=  newops->resauto;
   fop.geomauto	=  newops->geomauto;
   fop.width 	=  newops->width;
   fop.height 	=  newops->height;
   fop.fine 	=  newops->fine;
   fop.landscape=  newops->landscape;
   fop.flip 	=  newops->flip;
   fop.invert 	=  newops->invert;
   fop.lsbfirst =  newops->lsbfirst;
   fop.raw 	=  newops->raw;

   setFaxDefaults();

   writeSettings();
 }

 delete opd;
}

void setFaxDefaults(){

  // fop is called in readSettings, so this can't be
  // called after a TopLevel::readSettings()

  if(have_cmd_opt ) // we have commad line options all kfaxrc defaults are
    return;         // overridden

  if(fop.resauto == 1){
    defaultpage.vres = -1;
  }
  else{
    defaultpage.vres = fop.fine;
  }

  if(fop.geomauto == 1){
    defaultpage.width = 0;
    defaultpage.height = 0;
  }
  else{
    defaultpage.width = fop.width;
    defaultpage.height = fop.height;
  }

  if( fop.landscape)
    defaultpage.orient |= TURN_L;

  if(fop.flip)
     defaultpage.orient |= TURN_U;

  defaultpage.inverse =    fop.invert;
  defaultpage.lsbfirst = fop.lsbfirst;

  if(fop.raw == 2)
    defaultpage.expander = g32expand;
  if(fop.raw == 4)
    defaultpage.expander = g4expand;
  if((fop.raw != 4) && (fop.raw != 2) )
    defaultpage.expander = g31expand;

}

static const char *description =
        I18N_NOOP("KDE G3/G4 Fax Viewer");

static KCmdLineOptions options[] =
{
   {"f", 0, 0 },
   {"fine",         I18N_NOOP( "Fine resolution" ), 0 },
   {"n", 0, 0 },
   {"normal",       I18N_NOOP( "Normal resolution" ), 0 },
   {"height",       I18N_NOOP( "Height (number of fax lines)" ), 0 },
   {"w", 0, 0 },
   {"width",        I18N_NOOP( "Width (dots per fax line)" ), 0 },
   {"l", 0, 0 },
   {"landscape",    I18N_NOOP( "Turn image 90 degrees (landscape mode)" ), 0 },
   {"u", 0, 0 },
   {"upsidedown",   I18N_NOOP( "Turn image upside down" ), 0 },
   {"i", 0, 0 },
   {"invert",       I18N_NOOP( "Invert black and white." ), 0 },
   {"m", 0, 0 },
   {"mem <bytes>",  I18N_NOOP( "Limit memory use to 'bytes'." ), "8M" },
   {"r", 0, 0 },
   {"reverse",      I18N_NOOP( "Fax data is packed lsb first" ), 0 },
   {"2" ,       I18N_NOOP( "Raw files are g3-2d" ), 0 },
   {"4",        I18N_NOOP( "Raw files are g4" ), 0 },
   {"+file(s)",     I18N_NOOP( "Fax file(s) to show" ), 0 },
   { 0, 0, 0 },
};

int main (int argc, char **argv)
{
  KAboutData aboutData( "kfax", I18N_NOOP("KFax"),
      KFAXVERSION, description, KAboutData::License_GPL,
      "(c) 1997-98 Bernd Johannes Wuebben");
  aboutData.addAuthor( "Bernd Johannes Wuebben", 0, "wuebben@kde.org" );
  aboutData.addCredit( "Nadeem Hasan", I18N_NOOP( "UI Rewrite, lots of code "
      "cleanups and fixes" ), "nhasan@kde.org" );

  KCmdLineArgs::init(argc, argv, &aboutData);
  KCmdLineArgs::addCmdLineOptions( options );

  MyApp a;

  qtdisplay = qt_xdisplay();

  viewfaxmain();

  toplevel = new TopLevel();
  toplevel->show ();

  startingup = 1;
  a.processEvents();
  a.flushX();

  startingup = 0;

  faxinit();
  if(!have_no_fax){

    thispage = firstpage;

    toplevel->newPage();
    toplevel->resizeView();
    //TODO : I don't think I need this putImage();
    toplevel->putImage();
  }

  toplevel->uiUpdate();

  return a.exec ();
}

int copyfile(const char* toname,char* fromname)
{
  char buffer[4*1028];
  int count = 0;
  int count2;

  FILE*  fromfile;
  FILE*  tofile;

  if (QFile::exists(toname)) {
    if(KMessageBox::questionYesNo( 0,
			      i18n("A file with this name already exists.\n"
			      "Do you want to overwrite it?")))
      return 1;
  }

  if((fromfile = fopen(fromname,"r")) == NULL)
    return 0;

  if((tofile =  fopen(toname,"w")) == NULL){
    fclose(fromfile);
    return 0;
  }


  while((count = fread(buffer,sizeof(char),4*1028,fromfile))){
    count2 = fwrite(buffer,sizeof(char),count,tofile);
    if (count2 != count){
      fclose(fromfile);
      fclose(tofile);
      return 0;
    }

  }
  fclose(fromfile);
  fclose(tofile);

  return 1;
}
