  /*

    $Id: kedit.cpp,v 1.145 2001/07/02 14:08:49 pfeiffer 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.

    */

// WABA: Needed for backupFile()
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//

#include <qdragobject.h>
#include <qdir.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qtabdialog.h>
#include <qtextstream.h>
#include <qtextcodec.h>
#include <qtimer.h>
#include <qcolor.h>

#include <kapp.h>
#include <kcolordlg.h>
#include <kcursor.h>
#include "ktextfiledlg.h"
#include <kiconloader.h>
#include <kio/job.h>
#include <kio/netaccess.h>
#include <kglobal.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <ksavefile.h>
#include <ktempfile.h>
#include <kstdaccel.h>
#include <kspell.h>
#include <ksconfig.h>
#include <keditcl.h>
#include <kaboutdata.h>
#include <kcmdlineargs.h>
#include <kedittoolbar.h>
#include <kkeydialog.h>

#include "kedit.h"
#include "mail.h"
#include "optiondialog.h"
#include "version.h"

#include <kaction.h>
#include <kstdaction.h>
#include <kcharsets.h>
#include <kdebug.h>

#include <stdlib.h>

QList<TopLevel> *TopLevel::windowList = 0;

int default_open =  TopLevel::OPEN_READWRITE;

TopLevel::TopLevel (QWidget *, const char *name)
  : KMainWindow ( 0,name )
{
  mOptionDialog = 0;
  kspell = 0;
  newWindow = false;

  if (!windowList)
  {
     windowList = new QList<TopLevel>;
     windowList->setAutoDelete( FALSE );
  }
  windowList->append( this );

  statusbar_timer = new QTimer(this);
  connect(statusbar_timer, SIGNAL(timeout()),this,SLOT(timer_slot()));

  connect(kapp,SIGNAL(kdisplayPaletteChanged()),this,SLOT(set_colors()));

  setupStatusBar();
  setupActions();

  resize(550,400); // Default size

  readSettings();

  setupEditWidget();
  set_colors();

  setAcceptDrops(true);

  setFileCaption();
}


TopLevel::~TopLevel ()
{
  windowList->remove( this );
  delete mOptionDialog;
}

QString TopLevel::name()
{
  if (location.isEmpty())
     return i18n("[New Document]");
  return location;
}


void TopLevel::setupEditWidget()
{
  eframe = new KEdit (this, "eframe");
  KCursor::setAutoHideCursor( eframe, true );

  connect(eframe, SIGNAL(CursorPositionChanged()),this,
	  SLOT(statusbar_slot()));
  connect(eframe, SIGNAL(toggle_overwrite_signal()),this,
	  SLOT(toggle_overwrite()));
  connect(eframe, SIGNAL(gotUrlDrop(QDropEvent*)), this,
	  SLOT(urlDrop_slot(QDropEvent*)));
  connect(eframe, SIGNAL(undoAvailable(bool)),undoAction,
	  SLOT(setEnabled(bool)));
  connect(eframe, SIGNAL(redoAvailable(bool)),redoAction,
	  SLOT(setEnabled(bool)));
  connect(eframe, SIGNAL(copyAvailable(bool)),cutAction,
	  SLOT(setEnabled(bool)));
  connect(eframe, SIGNAL(copyAvailable(bool)),copyAction,
	  SLOT(setEnabled(bool)));

  undoAction->setEnabled(false);
  redoAction->setEnabled(false);
  cutAction->setEnabled(false);
  copyAction->setEnabled(false);

  setCentralWidget(eframe);
  eframe->setMinimumSize(200, 100);

  if( mOptionState.misc.wrapMode == SMiscState::fixedColumnWrap )
  {
    eframe->setWordWrap(QMultiLineEdit::FixedColumnWidth);
    eframe->setWrapColumnOrWidth(mOptionState.misc.wrapColumn);
  }
  else if( mOptionState.misc.wrapMode == SMiscState::dynamicWrap )
  {
    eframe->setWordWrap(QMultiLineEdit::WidgetWidth);
  }
  else
  {
    eframe->setWordWrap(QMultiLineEdit::NoWrap);
  }
  eframe->setFont( mOptionState.font.font );
  eframe->setModified(false);

  setSensitivity();

  eframe->setFocus();

  /*
  right_mouse_button = new QPopupMenu;

  right_mouse_button->insertItem (i18n("Open..."),
				  this, 	SLOT(file_open()));
  right_mouse_button->insertItem (i18n("Save"),
				  this, 	SLOT(file_save()));
  right_mouse_button->insertItem (i18n("Save as..."),
				  this, SLOT(file_save_as()));
  right_mouse_button->insertSeparator(-1);
  right_mouse_button->insertItem(i18n("Copy"),
				 this, SLOT(copy()));
  right_mouse_button->insertItem(i18n("Paste"),
				 this, SLOT(paste()));
  right_mouse_button->insertItem(i18n("Cut"),
				 this, SLOT(cut()));
  right_mouse_button->insertItem(i18n("Select All"),
				 this, SLOT(select_all()));
  eframe->installRBPopup(right_mouse_button);
  */
}


void TopLevel::setupActions()
{

    // setup File menu
    KStdAction::openNew(this, SLOT(file_new()), actionCollection());
    KStdAction::open(this, SLOT(file_open()), actionCollection());
    recent = KStdAction::openRecent(this, SLOT(openRecent(const KURL&)),
                                          actionCollection());
    KStdAction::save(this, SLOT(file_save()), actionCollection());
    KStdAction::saveAs(this, SLOT(file_save_as()), actionCollection());
    KStdAction::close(this, SLOT(file_close()), actionCollection());
    KStdAction::print(this, SLOT(print()), actionCollection());
    KStdAction::mail(this, SLOT(mail()), actionCollection());
    KStdAction::quit(this, SLOT(close()), actionCollection());

    // setup edit menu
    undoAction = KStdAction::undo(this, SLOT(undo()), actionCollection());
    redoAction = KStdAction::redo(this, SLOT(redo()), actionCollection());
    cutAction = KStdAction::cut(this, SLOT(cut()), actionCollection());
    copyAction = KStdAction::copy(this, SLOT(copy()), actionCollection());
    KStdAction::paste(this, SLOT(paste()), actionCollection());
    KStdAction::selectAll(this, SLOT(select_all()), actionCollection());
    KStdAction::find(this, SLOT(search()), actionCollection());
    KStdAction::findNext(this, SLOT(search_again()), actionCollection());
    KStdAction::replace(this, SLOT(replace()), actionCollection());

    (void)new KAction(i18n("&Insert File"), 0, this, SLOT(file_insert()),
                      actionCollection(), "insert_file");
    (void)new KAction(i18n("I&nsert Date"), 0, this, SLOT(insertDate()),
                      actionCollection(), "insert_date");
    (void)new KAction(i18n("Cl&ean Spaces"), 0, this, SLOT(clean_space()),
                      actionCollection(), "clean_spaces");

    // setup Tools menu
    KStdAction::spelling(this, SLOT(spellcheck()), actionCollection());

    // setup Go menu
    KStdAction::gotoLine(this, SLOT(gotoLine()), actionCollection());

    // setup Settings menu
    toolbarAction = KStdAction::showToolbar(this, SLOT(toggleToolBar()), actionCollection());
    statusbarAction = KStdAction::showStatusbar(this, SLOT(toggleStatusBar()), actionCollection());

    KStdAction::saveOptions(this, SLOT(save_options()), actionCollection());
    KStdAction::preferences(this, SLOT(customize()), actionCollection());

    KStdAction::keyBindings(this, SLOT(editKeys()), actionCollection());
    KStdAction::configureToolbars(this, SLOT(editToolbars()), actionCollection());

    createGUI();
}

void TopLevel::setupStatusBar()
{
  statusBar()->insertItem("", ID_GENERAL, 10 );
  statusBar()->insertFixedItem( i18n("OVR"), ID_INS_OVR );
  statusBar()->insertFixedItem( i18n("Line:000000 Col: 000"), ID_LINE_COLUMN );

  statusBar()->setItemAlignment( ID_GENERAL, AlignLeft|AlignVCenter );
  statusBar()->setItemAlignment( ID_LINE_COLUMN, AlignLeft|AlignVCenter );
  statusBar()->setItemAlignment( ID_INS_OVR, AlignLeft|AlignVCenter );

  statusBar()->changeItem( i18n("Line: 1 Col: 1"), ID_LINE_COLUMN );
  statusBar()->changeItem( i18n("INS"), ID_INS_OVR );
}




void TopLevel::saveProperties(KConfig* config)
{
  if(location.isEmpty() && !eframe->isModified())
    return;

  config->writeEntry("filename",name());
  config->writeEntry("modified",eframe->isModified());

  int line, col;
  eframe->getCursorPosition(&line, &col);
  config->writeEntry("current_line", line);
  config->writeEntry("current_column", col);

  if(eframe->isModified())
  {
    QString tmplocation = kapp->tempSaveName(name());
    saveFile(tmplocation, false);
  }
}


void TopLevel::readProperties(KConfig* config){

    QString filename = config->readEntry("filename","");
    int modified = config->readNumEntry("modified",0);
    int line = config->readNumEntry("current_line", 0);
    int col = config->readNumEntry("current_column", 0);

    if(!filename.isEmpty() && modified){
        bool ok;

        QString fn = kapp->checkRecoverFile(filename,ok);

	if(ok){
	  openFile(fn, OPEN_READWRITE);
	  location = filename;
	  eframe->setModified();
          eframe->setCursorPosition(line, col);
	  setFileCaption();
          statusbar_slot();
	}
    }
    else{

      if(!filename.isEmpty()){
	openFile(filename, OPEN_READWRITE);
        location = filename;
        eframe->setModified(false);
        eframe->setCursorPosition(line, col);
        setFileCaption();
        statusbar_slot();
      }
    }
}


void TopLevel::undo()
{
  eframe->undo();
}


void TopLevel::redo()
{
  eframe->redo();
}


void TopLevel::copy()
{
  eframe->copyText();
}


void TopLevel::select_all()
{
  eframe->selectAll();
}




void TopLevel::insertDate(){

  int line, column;

  QString string;
  QDate dt = QDate::currentDate();
  string = KGlobal::locale()->formatDate(dt);

  eframe->getCursorPosition(&line,&column);
  eframe->insertAt(string,line,column);
  eframe->setModified(TRUE);

  statusbar_slot();
}

void TopLevel::paste(){

 eframe->paste();
 eframe->setModified(TRUE);

 statusbar_slot();
}

void TopLevel::cut(){

 eframe->cut();
 eframe->setModified(TRUE);
 statusbar_slot();

}

void TopLevel::file_new()
{
  TopLevel *t = new TopLevel ();
  t->show();
  return;
}

void TopLevel::clean_space()
{
   if (!eframe);
   eframe->cleanWhiteSpace();
}

void TopLevel::spellcheck()
{
  if (!eframe) return;

  if (kspell) return; // In progress

  statusBar()->changeItem(i18n("Spellcheck:  Started."), ID_GENERAL);

  kspell = new KSpell(this, i18n("Spellcheck"), this,
	SLOT( spell_started(KSpell *)), &mOptionState.spell.config);

  connect (kspell, SIGNAL ( death()),
          this, SLOT ( spell_finished( )));

  connect (kspell, SIGNAL (progress (unsigned int)),
          this, SLOT (spell_progress (unsigned int)));
  connect (kspell, SIGNAL (misspelling (QString, QStringList *, unsigned)),
          eframe, SLOT (misspelling (QString, QStringList *, unsigned)));
  connect (kspell, SIGNAL (corrected (QString, QString, unsigned)),
          eframe, SLOT (corrected (QString, QString, unsigned)));
  connect (kspell, SIGNAL (done(const QString&)),
		 this, SLOT (spell_done(const QString&)));
}

void TopLevel::spell_started( KSpell *)
{
   eframe->spellcheck_start();
   kspell->setProgressResolution(2);
   kspell->check(eframe->text());
}

void TopLevel::spell_progress (unsigned int percent)
{
  QString s;
  s = i18n("Spellcheck:  %1% complete").arg(percent);

  statusBar()->changeItem (s, ID_GENERAL);
}

void TopLevel::spell_done(const QString& newtext)
{
  eframe->spellcheck_stop();
  if (kspell->dlgResult() == 0)
  {
     eframe->setText( newtext);
     statusBar()->changeItem (i18n("Spellcheck:  Aborted"), ID_GENERAL);
  }
  else
  {
     statusBar()->changeItem (i18n("Spellcheck:  Complete"), ID_GENERAL);
  }
  kspell->cleanUp();
}

void TopLevel::spell_finished( )
{
  KSpell::spellStatus status = kspell->status();
  delete kspell;
  kspell = 0;
  if (status == KSpell::Error)
  {
     KMessageBox::sorry(this, i18n("ISpell could not be started.\n"
     "Please make sure you have ISpell properly configured and in your PATH."));
  }
  else if (status == KSpell::Crashed)
  {
     eframe->spellcheck_stop();
     statusBar()->changeItem (i18n("Spellcheck:  Crashed"), ID_GENERAL);
     KMessageBox::sorry(this, i18n("ISpell seems to have crashed."));
  }
}

void TopLevel::file_open( void )
{
  while( 1 )
  {
    KURL url = KTextFileDialog::getOpenURLwithEncoding(
		QString::null, "*", this,
		i18n("Open File"));
    if( url.isEmpty() )
    {
      return;
    }

    TopLevel *toplevel;
    if( !location.isEmpty() || eframe->isModified() )
    {
      toplevel = new TopLevel();
      if( toplevel == 0 )
      {
	return;
      }
    }
    else
    {
      toplevel = this;
    }

    toplevel->setEncoding(KTextFileDialog::getEncodingForURL(url));

    QString tmpfile;
    KIO::NetAccess::download( url, tmpfile );
    int result = toplevel->openFile( tmpfile, 0 );
    KIO::NetAccess::removeTempFile( tmpfile );

    if( result == KEDIT_OK )
    {
      if( toplevel != this ) { toplevel->show(); }
      toplevel->location = url.path();
      toplevel->setFileCaption();
      recent->addURL( url );
      toplevel->eframe->setModified(false);
      toplevel->setGeneralStatusField(i18n("Done"));
      toplevel->statusbar_slot();
      break;
    }
    else if( result == KEDIT_RETRY )
    {
      if( toplevel != this ) { delete toplevel; }
    }
    else
    {
      if( toplevel != this ) { delete toplevel; }
      break;
    }
  }
}

void TopLevel::file_insert()
{
  while( 1 )
  {
    KURL url = KTextFileDialog::getOpenURLwithEncoding(
        QString::null, "*", this,
	i18n("Insert File") );
    if( url.isEmpty() )
    {
      return;
    }

    QString tmpfile;
    KIO::NetAccess::download( url, tmpfile );
    int result = openFile( tmpfile, OPEN_INSERT );
    KIO::NetAccess::removeTempFile( tmpfile );

    if( result == KEDIT_OK )
    {
      recent->addURL( url );
      eframe->setModified(true);
      setGeneralStatusField(i18n("Done"));
      statusbar_slot();
    }
    else if( result == KEDIT_RETRY )
    {
      continue;
    }
    return;
  }
}

bool TopLevel::queryExit()
{
  // save recent files menu
  config = kapp->config();
  recent->saveEntries( config );
  config->sync();

  return true;
}

bool TopLevel::queryClose()
{
    queryExit();
    int result;
  if( !eframe->isModified() )
  {
     return true;
  }

  QString msg = i18n(""
        "This document has been modified.\n"
        "Would you like to save it?" );
  switch( KMessageBox::warningYesNoCancel( this, msg ) )
  {
     case KMessageBox::Yes: // Save, then exit
                    if( location.isEmpty())
                        file_save_as();
                    else
                        {
	        result = saveFile(location);
	        if( result == KEDIT_USER_CANCEL )
	                return false;
	        if( result != KEDIT_OK)
	                {
	                msg = i18n(""
	                "Could not save the file.\n"
	                "Exit anyways?");
	                switch( KMessageBox::warningYesNo( this, msg ) )
	                        {
	                        case KMessageBox::Yes:
	                                return true;
	                        case KMessageBox::No:
                                        default:
                                                return false;
	                        }
	                }
                        }
          return true;

     case KMessageBox::No: // Don't save but exit.
          return true;

     case KMessageBox::Cancel: // Don't save and don't exit.
     default:
	  break; // Don't exit...
  }
  return false;
}



void TopLevel::openRecent(const KURL& url)
{
  if (!location.isEmpty() || eframe->isModified())
  {
     TopLevel *t = new TopLevel (0,0);
     t->show();
     t->openRecent(url);
     return;
  }
  openURL( url, OPEN_READWRITE );
}

void TopLevel::file_close()
{
  if( eframe->isModified() )
  {
    QString msg = i18n("This document has been modified.\n"
                       "Would you like to save it?" );
    switch( KMessageBox::warningYesNoCancel( this, msg ) )
    {
      case KMessageBox::Yes: // Save, then close
        file_save();
        if (eframe->isModified())
           return; // Error during save.
      break;

      case KMessageBox::No: // Don't save but close.
      break;

      case KMessageBox::Cancel: // Don't save and don't close.
	return;
      break;
    }
  }
  eframe->clear();
  eframe->setModified(false);
  location = QString::null;
  setFileCaption();
  statusbar_slot();
}


void TopLevel::file_save()
{
  if (location.isEmpty())
  {
     file_save_as();
     return;
  }

  KURL u( location );
  if ( !u.isMalformed() && u.protocol() != "file" )
  {
     url_location = location;
     saveURL( u );
     statusbar_slot();
     recent->addURL( u );
     return;
  }

  int result = KEDIT_OK;

  result =  saveFile(location); // error messages are handled by saveFile

  if ( result == KEDIT_OK ){
     QString string;
     string = i18n("Wrote: %1").arg(location);
     setGeneralStatusField(string);
  }
}

void TopLevel::setGeneralStatusField(const QString &text){

    statusbar_timer->stop();

    statusBar()->changeItem(text,ID_GENERAL);
    statusbar_timer->start(10000,TRUE); // single shot

}


void TopLevel::file_save_as()
{
  KURL u = KTextFileDialog::getSaveURLwithEncoding(
	      location, "*", this,
	      i18n("Save File As"),
	      enc);
  if (u.isEmpty())
    return;

  int result = saveURL(u); // error messages are handled by saveFile

  if ( result == KEDIT_OK )
    {
      location = u.url();
      setFileCaption();
      QString string = i18n("Saved as: %1").arg(location);
      setGeneralStatusField(string);
      recent->addURL( u );
    }
}



void TopLevel::mail()
{
  // TODO: add charset box to mail dlg?

  Mail *maildlg = new Mail(this,"maildialog");
  if( maildlg->exec() == QDialog::Rejected )
  {
    delete maildlg;
    return;
  }

  kapp->processEvents();
  kapp->flushX();

  QString cmd;
  cmd = cmd.sprintf(mOptionState.misc.mailCommand.local8Bit().data(),
    maildlg->getSubject().local8Bit().data(),
    maildlg->getRecipient().local8Bit().data());

  delete maildlg;

  FILE* mailpipe = popen(cmd.local8Bit().data(),"w");
  if( mailpipe == NULL )
  {
    QString msg = i18n(""
      "Could not pipe the contents"
      " of this document into:\n %1").arg(cmd);
    KMessageBox::sorry( this, msg );
    return;
  }

  QTextStream t(mailpipe,IO_WriteOnly );
  QTextCodec *codec;
  if (!enc.isEmpty())
    codec = QTextCodec::codecForName(enc.latin1());
  else
    codec = QTextCodec::codecForLocale();
  t.setCodec(codec);

  // t << "Content-Type: text/plain; charset=" << enc << "\n\n";

  int line_count = eframe->numLines();

  for(int i = 0 ; i < line_count ; i++){
    t << eframe->textLine(i) << '\n';
  }
  pclose(mailpipe);
}

/*
void TopLevel::fancyprint(){

  QPrinter prt;
  char buf[200];
  if ( prt.setup(0) ) {

    int y =10;
    QPainter p;
    p.begin( &prt );
    p.setFont( eframe->font() );
    QFontMetrics fm = p.fontMetrics();

    int numlines = eframe->numLines();
    for(int i = 0; i< numlines; i++){
      y += fm.ascent();
      QString line;
      line = eframe->textLine(i);
      line.replace( QRegExp("\t"), "        " );
      strncpy(buf,line.local8Bit(),160);
      for (int j = 0 ; j <150; j++){
	if (!isprint(buf[j]))
	    buf[j] = ' ';
      }
      buf[line.length()] = '\0';
      p.drawText( 10, y, buf );
      y += fm.descent();
    }

    p.end();
  }
  return ;
}
*/

void TopLevel::helpselected(){

  kapp->invokeHelp( );

}

void TopLevel::search(){

      eframe->search();
      statusbar_slot();
}

void TopLevel::replace(){

      eframe->replace();
      statusbar_slot();
}


void TopLevel::customize( void )
{
  if( mOptionDialog == 0 )
  {
    mOptionDialog = new COptionDialog( topLevelWidget(), 0, false );
    if( mOptionDialog == 0 ) { return; }
    connect( mOptionDialog, SIGNAL(fontChoice(const SFontState &)),
	     this, SLOT(setFontOption(const SFontState &)) );
    connect( mOptionDialog, SIGNAL(colorChoice(const SColorState &)),
	     this, SLOT(setColorOption(const SColorState &)) );
    connect( mOptionDialog, SIGNAL(spellChoice(const SSpellState &)),
	     this, SLOT(setSpellOption(const SSpellState &)) );
    connect( mOptionDialog, SIGNAL(miscChoice(const SMiscState &)),
	     this, SLOT(setMiscOption(const SMiscState &)) );
    connect( mOptionDialog, SIGNAL(save()),
	     this, SLOT(writeSettings()) );
  }
  if( mOptionDialog->isVisible() == false )
  {
    mOptionDialog->setState( mOptionState );
  }

  mOptionDialog->show();
}

void TopLevel::editKeys()
{
  KKeyDialog::configureKeys(actionCollection(), xmlFile());
}

void TopLevel::editToolbars()
{
  KEditToolbar dlg(actionCollection());

  if (dlg.exec())
    createGUI();
}

void TopLevel::setFontOption( const SFontState &font )
{
  mOptionState.font = font;
  eframe->setFont( mOptionState.font.font );
}


void TopLevel::setColorOption( const SColorState &color )
{
  mOptionState.color = color;
  set_colors();
}


void TopLevel::setSpellOption( const SSpellState &spell )
{
  mOptionState.spell = spell;
}


void TopLevel::setMiscOption( const SMiscState &misc )
{
  mOptionState.misc = misc;
  if( mOptionState.misc.wrapMode == SMiscState::fixedColumnWrap )
  {
    eframe->setWordWrap(QMultiLineEdit::FixedColumnWidth);
    eframe->setWrapColumnOrWidth(mOptionState.misc.wrapColumn);
  }
  else if( mOptionState.misc.wrapMode == SMiscState::dynamicWrap )
  {
    eframe->setWordWrap(QMultiLineEdit::WidgetWidth);
  }
  else
  {
    eframe->setWordWrap(QMultiLineEdit::NoWrap);
  }

  //eframe->saveBackupCopy(mOptionState.misc.backupCheck);
}



void TopLevel::toggleToolBar()
{
  bool show_toolbar = toolbarAction->isChecked();
  if (show_toolbar)
     toolBar("mainToolBar")->show();
  else
     toolBar("mainToolBar")->hide();
}


void TopLevel::toggleStatusBar()
{
  bool show_statusbar = statusbarAction->isChecked();
  if (show_statusbar)
     statusBar()->show();
  else
     statusBar()->hide();
}


void TopLevel::search_again()
{
  eframe->repeatSearch();
  statusbar_slot();
}


void TopLevel::setFileCaption(){

  setCaption(name());
}


void TopLevel::gotoLine() {
	eframe->doGotoLine();
}

void TopLevel::statusbar_slot(){

  QString linenumber;

  linenumber = i18n("Line: %1 Col: %2")
		     .arg(eframe->currentLine() + 1)
		     .arg(eframe->currentColumn() +1);

  statusBar()->changeItem(linenumber,ID_LINE_COLUMN);
}

void TopLevel::print()
{
  QString command;
  QString com;
  QString file;

  int result;

  if( eframe->isModified() )
  {
    QString msg = i18n(""
      "The current document has been modified.\n"
      "Would you like to save the changes before\n"
      "printing this document?");
    switch( KMessageBox::warningYesNoCancel( this, msg ) )
    {
      case KMessageBox::Yes: // Save, then print
                if(location.isEmpty())
                        file_save_as();
                else
                        {
 	        result = saveFile(location);
	        if( result == KEDIT_USER_CANCEL )
	                {
	                return;
	                }
	        if( result != KEDIT_OK)
	        {
	        msg = i18n(""
	                "Could not save the file.\n"
	                "Print anyways?");
	        switch( KMessageBox::warningYesNo( this, msg ) )
	                {
	                case KMessageBox::Yes:
	                        break;

	                case KMessageBox::No:
	                        return;
	                        break;
	                }
	        }
                }
      break;

      case KMessageBox::No: // Don't save but print.
      break;

      case KMessageBox::Cancel: // Don't save and don't print.
	return;
      break;
    }
  }

  PrintDialog *printDialog = new PrintDialog(this,"print",true);
  printDialog->setWidgets(pi);

  if(printDialog->exec() == QDialog::Rejected )
  {
    delete printDialog;
    return;
  }

  pi = printDialog->getCommand();
  delete printDialog;

  KTempFile tmpf;
  tmpf.setAutoDelete(true);
  if ( tmpf.status() )
  {
     KMessageBox::error( this,
			 i18n("Printing failed because the document "
			      "could not be saved to a temporary file.  "
			      "Please save the document first!"),
			 i18n("Printing Error") );
     return;
  }
  if( location.isEmpty() )
  {
    //
    // we go through all of this so that we can print an "Untitled" document
    // quickly without going through the hassle of saving it. This will
    // however result in a temporary filename and your printout will
    // usually show that temp name, such as /tmp/00432aaa
    // for a non "untitled" document we don't want that to happen so
    // we asked the user to save before we print the document.
    //
    // TODO find a smarter solution for the above!
    //
    QString tmpname = tmpf.name();
    QFile file(tmpname);
    file.open(IO_WriteOnly);


    if(pi.selection)
    {
      if(file.writeBlock(eframe->markedText().ascii(),
			 eframe->markedText().length()) == -1)
      {
	result = KEDIT_OS_ERROR;
      }
      else
      {
	result = KEDIT_OK;
      }
    }
    else
    {
      if(file.writeBlock(eframe->text().ascii(),
			 eframe->text().length()) == -1)
      {
	result = KEDIT_OS_ERROR;
      }
      else
      {
	result = KEDIT_OK;
      }
    }

    file.close();
    // TODO error handling

    if (pi.raw)
    {
      command = "lpr";
    }
    else
    {
      command = pi.command;
    }
    com = QString("%1 %2 ; rm %3 &").arg(command).arg(tmpname).arg(tmpname);
    system(com.ascii());
    QString string;
    if(pi.selection)
      string = i18n("Printing: %1 Untitled (Selection)").arg(command);
    else
      string = i18n("Printing: %1 Untitled").arg(command);
    setGeneralStatusField(string);
  }
  else // document name != Untiteled
  {
    QString tmpname = tmpf.name();
    if( pi.selection )
    {
      // print only the selection
      QFile file(tmpname);
      file.open(IO_WriteOnly);


      if(file.writeBlock(eframe->markedText().ascii(),
			 eframe->markedText().length()) == -1)
      {
	result = KEDIT_OS_ERROR;
      }
      file.close();
      // TODO error handling
    }

    if(pi.raw)
    {
      command = "lpr";
    }
    else
    {
      command = pi.command;
    }

    if(!pi.selection) // print the whole file
    {
      //com = QString ("%1 '%2' &").arg(command).arg(location);
      QString tmpFile;
      if( KIO::NetAccess::download( location, tmpFile ) )
	{
	  com = QString ("%1 '%2' &").arg(command).arg(tmpFile);
	  system(com.ascii());
	  KIO::NetAccess::removeTempFile( tmpFile );
	  QString string;
	  string = i18n("Printing: %1").arg(com);
	  setGeneralStatusField(string);
       }
    }
    else // print only the selection
    {
      com = QString ("%1 %2 ; rm %3 &").arg(command).arg(tmpname).arg(tmpname);
      system(com.ascii());
      QString string = i18n("Printing: %1 %2 (Selection)")
	.arg(command).arg(location);
      setGeneralStatusField(string);
    }
    //kdDebug(0) << com.ascii() << endl;
  }
}



void TopLevel::setSensitivity (){

}

void TopLevel::save_options()
{
  KConfig *config = kapp->config();
  config->setGroup("MainWindow");
  saveMainWindowSettings(config);
}

int TopLevel::saveURL( const KURL& _url )
{
    if ( _url.isMalformed() )
    {
        KMessageBox::sorry(this, i18n("Malformed URL"));
        return KEDIT_RETRY;
    }

    // Just a usual file ?
    if ( _url.isLocalFile() )
    {
        return saveFile( _url.url() );
    }

    KTempFile tmpFile;
    tmpFile.setAutoDelete(true);
    eframe->setModified( true );
    saveFile( tmpFile.name(), false );

    if (KIO::NetAccess::upload( tmpFile.name(), _url ) == false)
    {
      KMessageBox::error(this, "Could not save remote file");
      return KEDIT_RETRY;
    }

    return true;
}

int TopLevel::openFile( const QString& _url, int _mode )
{
  location=_url;
  setFileCaption();
  KURL *u = new KURL( _url );
  if ( u->isMalformed() )
  {
     KMessageBox::sorry(this, i18n("This is not a valid filename.\n"));
     return KEDIT_RETRY;
  }
  if ( !u->isLocalFile() )
  {
     KMessageBox::sorry(this, i18n("This is not a local file.\n"));
     return KEDIT_RETRY;
  }

  QFileInfo info(u->path());

  if(!info.exists())
  {
     if ((_mode & OPEN_NEW) != 0)
        return KEDIT_OK;
     KMessageBox::sorry(this, i18n("The specified file does not exist"));
     return KEDIT_RETRY;
  }

  if(info.isDir())
  {
     KMessageBox::sorry(this, i18n("You have specified a directory"));
     return KEDIT_RETRY;
  }

  QFile file(u->path());

  if(!file.open(IO_ReadOnly))
  {
     KMessageBox::sorry(this, i18n("You do not have read permission to this file."));
     return KEDIT_RETRY;
  }

  QTextStream stream(&file);
  QTextCodec *codec;
  if (!enc.isEmpty())
    codec = QTextCodec::codecForName(enc.latin1());
  else
    codec = QTextCodec::codecForLocale();
  stream.setCodec(codec);

  if ((_mode & OPEN_INSERT) == 0)
  {
     eframe->clear();
  }
  eframe->insertText( &stream );
  eframe->setModified(false);
  return KEDIT_OK;

}

static int
write_all(int fd, const char *buf, size_t len)
{
   while (len > 0)
   {
      int written = write(fd, buf, len);
      if (written < 0)
      {
          if (errno == EINTR)
             continue;
          return -1;
      }
      buf += written;
      len -= written;
   }
   return 0;
}

static bool backupFile( const QString &qFilename)
{
   QCString cFilename = QFile::encodeName(qFilename);
   QCString cBackup =cFilename + "~";
   const char *filename = cFilename.data();
   const char *backup = cBackup.data();

   int fd = open( filename, O_RDONLY);
   if (fd < 0)
      return false;

   struct stat buff;
   if ( fstat( fd, &buff) < 0 )
   {
      close( fd );
      return false;
   }

   int permissions = buff.st_mode & 07777;

   if ( stat( backup, &buff) == 0)
   {
      if ( unlink( backup ) != 0 )
      {
         close(fd);
         return false;
      }
   }


   mode_t old_umask = umask(0);
   int fd2 = open( backup, O_WRONLY | O_CREAT | O_EXCL, permissions | S_IWUSR);
   umask(old_umask);

   if ( fd2 < 0 )
   {
      close(fd);
      return false;
   }

    char buffer[ 32*1024 ];

    while( 1 )
    {
       int n = ::read( fd, buffer, 32*1024 );
       if (n == -1)
       {
          if (errno == EINTR)
              continue;
          close(fd);
          close(fd2);
          return false;
       }
       if (n == 0)
          break; // Finished

       if (write_all( fd2, buffer, n))
       {
          close(fd);
          close(fd2);
          return false;
       }
    }

    close( fd );

    if (close(fd2))
    {
        return false;
    }
    return true;
}


int TopLevel::saveFile( const QString& _url, bool backup )
{
  KURL *u = new KURL( _url );
  if ( u->isMalformed() )
  {
     KMessageBox::sorry(this, i18n("This is not a valid filename.\n"));
     return KEDIT_RETRY;
  }
  if ( !u->isLocalFile() )
  {
     KMessageBox::sorry(this, i18n("This is not a local file.\n"));
     return KEDIT_RETRY;
  }

  QFileInfo info(u->path());

  if(info.isDir())
  {
     KMessageBox::sorry(this, i18n("You have specified a directory"));
     return KEDIT_RETRY;
  }

  if (backup && mOptionState.misc.backupCheck && QFile::exists(u->path()))
  {
     if (!backupFile(u->path()))
     {
        KMessageBox::sorry(this, i18n("Unable to make a backup of the original file."));
     }
  }

  // WABA: We don't use KSaveFile because it doesn't preserve hard/soft
  // links when saving. Most applications don't care about this, but an
  // editor is supposed to preserve such things.

  QFile file(u->path());
  if(!file.open(IO_WriteOnly))
  {
     KMessageBox::sorry(this, i18n("Unable to write to file."));
     return KEDIT_RETRY;
  }

  QTextStream textStream(&file);
  QTextCodec *codec;
  if (!enc.isEmpty())
    codec = QTextCodec::codecForName(enc.latin1());
  else
    codec = QTextCodec::codecForLocale();
  textStream.setCodec(codec);

  eframe->saveText( &textStream );
  file.close();

  if(file.status())
  {
     KMessageBox::sorry(this, i18n("Could not save file."));
     return KEDIT_RETRY;
  }
  eframe->setModified(false);
  return KEDIT_OK;
}


void TopLevel::openURL( const KURL& _url, int _mode )
{
    QString netFile = _url.url();
    kdDebug() << "TopLEvel::openUrl: " << netFile << endl;
    if ( _url.isMalformed() )
    {
        QString string;
        string = i18n( "Malformed URL\n%1").arg(netFile);

        KMessageBox::sorry(this, string);
        return;
    }

    QString target;
    if (KIO::NetAccess::download(_url, target) == false)
    {
      // this needs to be handled within KIO sooooon
      KMessageBox::error(this, i18n("Cannot download file!"));
      return;
    }

    setEncoding(KTextFileDialog::getEncodingForURL(_url));

    int result = openFile(target, 0);
    if (result == KEDIT_OK)
    {
        location = netFile;
        setFileCaption();
        recent->addURL(_url);
        eframe->setModified(false);
        setGeneralStatusField(i18n("Done"));
    }
}

void TopLevel::urlDrop_slot(QDropEvent* e) {

  dropEvent(e);
}

void TopLevel::dragEnterEvent(QDragEnterEvent* e)
{
  e->accept(QUriDrag::canDecode(e));
}

void TopLevel::dropEvent(QDropEvent* e)
{

    QStrList list;

    // This should never happen, but anyway...
    if(!QUriDrag::decode(e, list))
        return;

    char *s;

    for ( s = list.first(); s != 0L; s = list.next() )
    {
	// Load the first file in this window
	if ( s == list.getFirst() && !eframe->isModified() )
	{
            openURL( KURL(s), OPEN_READWRITE );
	}
	else
	{
	    setGeneralStatusField(i18n("New Window"));
	    TopLevel *t = new TopLevel ();
	    t->show ();
	    setGeneralStatusField(i18n("New Window Created"));
		t->openURL( KURL(s), OPEN_READWRITE );
	    setGeneralStatusField(i18n("Load Command Done"));
	}
    }
}

void TopLevel::timer_slot(){

  statusBar()->changeItem("",ID_GENERAL);

}



void TopLevel::set_colors()
{
  QPalette mypalette = (eframe->palette()).copy();

  QColorGroup ncgrp( mypalette.active() );

  if (mOptionState.color.custom)
  {
     ncgrp.setColor(QColorGroup::Text, mOptionState.color.textFg);
     ncgrp.setColor(QColorGroup::Base, mOptionState.color.textBg);
  }
  else
  {
     ncgrp.setColor(QColorGroup::Text, KGlobalSettings::textColor());
     ncgrp.setColor(QColorGroup::Base, KGlobalSettings::baseColor());
  }

  mypalette.setActive(ncgrp);
  mypalette.setDisabled(ncgrp);
  mypalette.setInactive(ncgrp);

  eframe->setPalette(mypalette);
}


void TopLevel::readSettings( void )
{
  //
  // Default settings. The values in mOptionState is defined in
  // optionstate.h
  //
  pi.command = "enscript -2rG";
  pi.raw = 1;
  pi.selection = 0;

  QString str;
  config = kapp->config();

  config->setGroup( "Text Font" );
  mOptionState.font.font = config->readFontEntry("KEditFont",
    &mOptionState.font.font);

  recent->loadEntries( config );
  config->setGroup("General Options");
  url_location = config->readEntry( "default_url",
    "ftp://localhost/welcome.msg" );

  mOptionState.misc.mailCommand = config->readEntry("MailCmd",
    mOptionState.misc.mailCommand );

  str = config->readEntry("WrapMode");
  if( str.isNull() == false )
    mOptionState.misc.wrapMode = str.toInt();

  str = config->readEntry("WrapColumn");
  if( str.isNull() == false )
    mOptionState.misc.wrapColumn = str.toInt();

  str = config->readEntry("BackupCopies");
  if( str.isNull() == false )
    mOptionState.misc.backupCheck = (bool) str.toInt();

  mOptionState.color.custom = config->readBoolEntry( "CustomColor",
	mOptionState.color.custom );

  str = config->readEntry( "ForeColor" );
  if( str.isNull() == false )
    mOptionState.color.textFg.setNamedColor( str );

  str = config->readEntry( "BackColor" );
  if( str.isNull() == false )
    mOptionState.color.textBg.setNamedColor( str );

  ///////////////////////////////////////////////////

  config->setGroup("MainWindow");
  applyMainWindowSettings(config);
  statusbarAction->setChecked(!statusBar()->isHidden());
  toolbarAction->setChecked(!toolBar()->isHidden());

  ///////////////////////////////////////////////////

  config->setGroup("Printing");
  str = config->readEntry("PrntCmd1");
  if ( str.isNull() == false )
    pi.command = str;

  str = config->readEntry("PrintSelection");
  if ( str.isNull() == false )
    pi.selection = str.toInt();

  str = config->readEntry("PrintRaw");
  if ( str.isNull() == false )
    pi.raw = str.toInt();

}

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

  config->setGroup( "Text Font" );
  config->writeEntry("KEditFont", mOptionState.font.font );

  recent->saveEntries( config );

  config->setGroup("MainWindow");
  saveMainWindowSettings(config);

  config->setGroup("General Options");
  config->writeEntry("default_url", url_location);

  config->writeEntry("MailCmd", mOptionState.misc.mailCommand );

  QString string;
  string.setNum( mOptionState.misc.wrapMode );
  config->writeEntry("WrapMode", string);

  string.setNum( mOptionState.misc.wrapColumn );
  config->writeEntry("WrapColumn", string);

  string="";
  string.setNum( (int) mOptionState.misc.backupCheck );
  config->writeEntry("BackupCopies", string);

  config->writeEntry("CustomColor", mOptionState.color.custom);

  QColor &fg = mOptionState.color.textFg;
  string.sprintf("#%02x%02x%02x", fg.red(), fg.green(), fg.blue() );
  config->writeEntry( "ForeColor", string );

  QColor &bg = mOptionState.color.textBg;
  string.sprintf("#%02x%02x%02x", bg.red(),bg.green(), bg.blue());
  config->writeEntry( "BackColor", string );

  ////////////////////////////////////////////////////

  config->setGroup("Printing");

  config->writeEntry("PrntCmd1", pi.command);

  string.setNum( pi.selection );
  config->writeEntry("PrintSelection", string);

  string.setNum( pi.raw );
  config->writeEntry("PrintRaw", string);

  config->sync();
}


void TopLevel::toggle_overwrite(){

  if(eframe->isOverwriteMode()){
    statusBar()->changeItem("OVR",ID_INS_OVR);
  }
  else{
    statusBar()->changeItem("INS",ID_INS_OVR);
  }

}

static const char *description=I18N_NOOP("A KDE Text Editor");

static const KCmdLineOptions options[] =
{
	{ "+file", I18N_NOOP("File or URL to Open"), 0 },
	{ 0, 0, 0}
};

int main (int argc, char **argv)
{
	bool have_top_window = false;

	KAboutData aboutData( "kedit", I18N_NOOP("KEdit"),
		KEDITVERSION, description, KAboutData::License_GPL,
		"(c) 1997-2000, Bernd Johannes Wuebben");
	aboutData.addAuthor("Bernd Johannes Wuebben",0, "wuebben@kde.org");
	KCmdLineArgs::init( argc, argv, &aboutData );
	KCmdLineArgs::addCmdLineOptions( options );

	KApplication a;
	//CT KIO::Job::initStatic();

	if ( a.isRestored() )
	{
		int n = 1;

		while (TopLevel::canBeRestored(n))
		{
			TopLevel *tl = new TopLevel();
			tl->restore(n);
			n++;
			have_top_window = true;
		}
	}
	else
	{
		have_top_window = false;
		KCmdLineArgs *args = KCmdLineArgs::parsedArgs();

		for(int i = 0; i < args->count(); i++)
		{
			TopLevel *t = new TopLevel;
			t->show ();
			have_top_window = true;

			KURL url = args->url(i);
			if( url.isLocalFile() ) {
			  t->setEncoding(KTextFileDialog::
					 getEncodingForURL(url));
			  t->openFile(url.path(),
				      default_open | TopLevel::OPEN_NEW);
			} else
			  t->openURL( url, default_open );
		}
		args->clear();
	}

	if(!have_top_window)
	{
		TopLevel *t = new TopLevel ();
		t->show ();
	}

	return a.exec ();
}




#include "kedit.moc"

