/***************************************************************************
                          quantaview.cpp  -  description
                             -------------------
    begin                : ���� 9 13:29:57 EEST 2000
    copyright            : (C) 2000 by Dmitry Poplavsky & Alexander Yakovlev & Eric Laffoon <pdima@users.sourceforge.net,yshurik@linuxfan.com,sequitur@easystreet.com>
                           (C) 2001-2005 Andras Mantia <amantia@kde.org>
 ***************************************************************************/

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

// include files for TQt
#include <tqprinter.h>
#include <tqpainter.h>
#include <tqtabbar.h>
#include <tqtabwidget.h>
#include <tqtimer.h>
#include <tqlayout.h>
#include <tqwidgetstack.h>
#include <tqdom.h>
#include <tqfile.h>
#include <tqevent.h>
#include <tqwidget.h>
#include <tqsplitter.h>
#include <tqpoint.h>
#include <tqscrollview.h>

// include files for KDE
#include <tdeaction.h>
#include <kdebug.h>
#include <kdirwatch.h>
#include <tdehtmlview.h>
#include <tdelocale.h>
#include <kstandarddirs.h>
#include <tdemenubar.h>
#include <kiconloader.h>
#include <tdemessagebox.h>
#include <kurldrag.h>
#include <tdeversion.h>
#include <tdeparts/partmanager.h>
#include <kstatusbar.h>

#include "undoredo.h"
#include "kafkacommon.h"
#include "wkafkapart.h"

#include <tdetexteditor/document.h>
#include <tdetexteditor/selectioninterface.h>
#include <tdetexteditor/selectioninterfaceext.h>
#include <tdetexteditor/view.h>
#include <tdetexteditor/viewcursorinterface.h>

// application specific includes
#include "document.h"
#include "resource.h"
#include "quantaview.h"
#include "quanta.h"
#include "quantacommon.h"
#include "qextfileinfo.h"
#include "viewmanager.h"

#include "tagaction.h"
#include "toolbartabwidget.h"
#include "quantaplugin.h"
#include "project.h"
#include "structtreeview.h"

#include "tagdialog.h"

extern int NN;
extern TQValueList<Node*> nodes;

QuantaView::QuantaView(TQWidget *parent, const char *name, const TQString &caption )
  : KMdiChildView(parent, name)
  , m_document(0L)
  , m_plugin(0L)
  , m_customWidget(0L)
  , m_kafkaDocument(0L)
  , m_currentFocus(SourceFocus)
{
  setMDICaption(caption);
//Connect the VPL update timers
  connect(&m_sourceUpdateTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(sourceUpdateTimerTimeout()));
  connect(&m_VPLUpdateTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(VPLUpdateTimerTimeout()));

//create the source and VPL holding widgets
  m_documentArea = new TQWidget(this);

//get the reference to the user toolbar holding widget
  ToolbarTabWidget *m_toolbarTab = ToolbarTabWidget::ref();
  m_toolbarTab->reparent(this, 0, TQPoint(), true);
  m_toolbarTab ->setFocusPolicy( TQWidget::NoFocus );

//create a splitter to separate the VPL and document area
  m_splitter = new TQSplitter(TQt::Vertical, this);
//place the widgets in a grid
  m_viewLayout = new TQGridLayout(this, 2, 0);
  m_viewLayout->setRowStretch(0, 0);
  m_viewLayout->setRowStretch(1,1);
  m_viewLayout->addWidget( m_toolbarTab, 0, 0);
  m_viewLayout->addWidget( m_documentArea, 1, 0);

  m_documentArea->show();

  setAcceptDrops(true); // [MB02] Accept drops on the view
}

QuantaView::~QuantaView()
{
   // quantaApp is undefined if the destructor of QuantaApp is active
   if (quantaApp)
     quantaApp->slotFileClosed(m_document);
   if (m_document)
   {
     m_document->view()->reparent(0L, 0, TQPoint(), false);
     if (quantaApp)
       emit documentClosed(m_document->url());
   }
   delete m_document;
   m_document = 0L;
}

bool QuantaView::mayRemove()
{
  emit hidePreview();
  if (m_plugin)
  {
    m_plugin->unload(false);
  } else
  {
    bool unmodifiedUntitled = false;
    if (m_document && m_document->isUntitled() && !m_document->isModified())
      unmodifiedUntitled = true;
    if (m_customWidget)
        m_customWidget->reparent(0L, 0, TQPoint(), false);
    if (!saveModified())
          return false;
    slotSetSourceLayout();
    if (static_cast<QuantaView *>(quantaApp->activeWindow()) == this)
    {
        parser->setSAParserEnabled(false);
        kdDebug(24000) << "Node objects before delete = " << NN << " ; list count = " << nodes.count() << endl;
        Node::deleteNode(baseNode);
        baseNode = 0L;
        kdDebug(24000) << "Node objects after delete = " << NN << " ; list count = " << nodes.count() << endl;
        TQValueList<Node*> nList = nodes;
/*        for (TQValueList<Node*>::ConstIterator it = nList.constBegin(); it != nList.constEnd(); ++it)
        Node::deleteNode(*it);
        kdDebug(24000) << "Node objects after cleanup = " << NN << " ; list count = " << nodes.count() << endl;*/
    }
    if (m_document)
    {
      KURL url = m_document->url();
      Project::ref()->saveBookmarks(url, dynamic_cast<KTextEditor::MarkInterface*>(m_document->doc()));
      if (!unmodifiedUntitled)
        emit eventHappened("before_close", url.url(), TQString());
      m_currentViewsLayout = -1;
//      m_document->closeTempFile();
      if (!m_document->isUntitled() && url.isLocalFile())
      {
        fileWatcher->removeFile(url.path());
//        kdDebug(24000) << "removeFile[mayRemove]: " << url.path() << endl;
      }
      Project::ref()->saveCursorPosition(url, dynamic_cast<KTextEditor::ViewCursorInterface*>(m_document->view()));

      quantaApp->menuBar()->activateItemAt(-1);
      quantaApp->guiFactory()->removeClient(m_document->view());
      if (!unmodifiedUntitled)
        emit eventHappened("after_close", url.url(), TQString());
    }
/*      kdDebug(24000) << "Calling reparse from close " << endl;
      parser->setSAParserEnabled(true);
      quantaApp->reparse(true);*/
  }
  return true;
}

void QuantaView::addDocument(Document *document)
{
   if (!document)
     return;
   m_document = document;
   connect(m_document, TQ_SIGNAL(editorGotFocus()), this, TQ_SLOT(slotSourceGetFocus()));
   connect(m_document->view(), TQ_SIGNAL(cursorPositionChanged()), this, TQ_SIGNAL(cursorPositionChanged()));


   m_kafkaDocument = KafkaDocument::ref();

  connect(m_kafkaDocument->getKafkaWidget(), TQ_SIGNAL(hasFocus(bool)),
    this, TQ_SLOT(slotVPLGetFocus(bool)));
  connect(m_kafkaDocument, TQ_SIGNAL(newCursorPosition(int,int)),
    this, TQ_SLOT(slotSetCursorPositionInSource(int, int)));
  connect(m_kafkaDocument, TQ_SIGNAL(loadingError(Node *)),
    this, TQ_SLOT(slotVPLLoadingError(Node *)));

   m_kafkaReloadingEnabled = true;
   m_quantaReloadingEnabled = true;
   m_curCol = m_curLine = m_curOffset = 0;

//init the VPL part
  m_currentViewsLayout = SourceOnly;//to correctly reload the timers.

  reloadUpdateTimers();

   m_currentViewsLayout = -1; //force loading of this layout
   slotSetSourceLayout();
}

void QuantaView::addPlugin(QuantaPlugin *plugin)
{
   ToolbarTabWidget *m_toolbarTab = ToolbarTabWidget::ref();
   m_toolbarTab->reparent(0, 0, TQPoint(), false);
   m_plugin = plugin;
   m_splitter->hide();
   TQWidget *w = m_plugin->widget();
   if (w)
   {
       w->reparent(m_documentArea, 0, TQPoint(), true);
       w->resize(m_documentArea->size());
   }
   m_documentArea->reparent(this, 0, TQPoint(), true);
   m_viewLayout->addWidget(m_documentArea, 1, 0);
   activated();
   updateTab();
}

void QuantaView::addCustomWidget(TQWidget *widget, const TQString &label)
{
   if (widget)
   {
      ToolbarTabWidget::ref()->reparent(0, 0, TQPoint(), false);
      m_customWidget = widget;
      m_splitter->hide();
      widget->reparent(m_documentArea, 0, TQPoint(), true);
      widget->resize(m_documentArea->size());
      if (!label.isEmpty())
      {
          widget->setCaption(label);
          updateTab();
      }
      m_viewLayout->addWidget(m_documentArea, 1, 0);
      m_documentArea->show();
   } else
   if (m_customWidget)
   {
      ToolbarTabWidget::ref()->reparent(this, 0, TQPoint(), qConfig.enableDTDToolbar);
      m_viewLayout->addWidget(ToolbarTabWidget::ref(), 0 , 0);
      m_customWidget = 0L; //avoid infinite recursion
      reloadLayout();
   }
  if (m_documentArea->height() + ToolbarTabWidget::ref()->height() > height() && ToolbarTabWidget::ref()->isVisible())
    resize(m_documentArea->width(), m_documentArea->height() - ToolbarTabWidget::ref()->height());
  else if (ToolbarTabWidget::ref()->isHidden())
    resize(width(), height());
}

void QuantaView::reloadLayout()
{
  int currentViewsLayout = m_currentViewsLayout;
  m_currentViewsLayout = -1; //force loading of this layout
  switch (currentViewsLayout)
  {
        case SourceOnly:
            slotSetSourceLayout();
            break;
        case SourceAndVPL:
            slotSetSourceAndVPLLayout();
            break;
        case VPLOnly:
            slotSetVPLOnlyLayout();
            break;
  }
}

void QuantaView::updateTab()
{
    if (qConfig.showCloseButtons == "ShowAlways")
    {
        setIcon(SmallIcon("window-close"));
    }
    if (m_document)
    {
        // try to set the icon from mimetype
        TQIconSet mimeIcon (KMimeType::pixmapForURL(m_document->url(), 0, TDEIcon::Small));
        if (mimeIcon.isNull())
          mimeIcon = TQIconSet(SmallIcon("text-x-generic"));
        TQString urlStr = QExtFileInfo::shortName(m_document->url().path());
        if (m_document->isModified())
        {
            if (qConfig.showCloseButtons == "ShowAlways")
            {
               setMDICaption(urlStr + " " + i18n("[modified]"));
            } else
            {
                setIcon(SmallIcon("document-save"));
                setMDICaption(urlStr);
            }
            m_szCaption = urlStr + " " + i18n("[modified]");
        } else
        {
          if (qConfig.showCloseButtons != "ShowAlways")
          {
             setIcon(mimeIcon.pixmap());
          }
          setMDICaption(urlStr);
          quantaApp->setTabToolTip(this, m_document->url().prettyURL(0, KURL::StripFileProtocol));
        }
    } else
    if (m_plugin)
    {
        if (qConfig.showCloseButtons != "ShowAlways")
        {
            setIcon(SmallIcon(m_plugin->icon()));
        }
       setMDICaption(m_plugin->name());
    } else
    if (m_customWidget)
    {
        if (qConfig.showCloseButtons != "ShowAlways")
        {
            setIcon(*(m_customWidget->icon()));
        }
        setMDICaption(m_customWidget->caption());
    }
}

TQString QuantaView::tabName()
{
   if (m_document)
   {
       return m_document->url().fileName();
   } else
   if (m_plugin)
   {
       return m_plugin->name();
   } else
   if (m_customWidget)
   {
       return m_customWidget->caption();
   } else
   return "";
}

void QuantaView::slotSetSourceLayout()
{
   emit hidePreview();
   if (m_currentViewsLayout == SourceOnly || !m_document)
     return;

   if(m_currentViewsLayout == SourceAndVPL)
     m_splitterSizes = m_splitter->sizes();

   TDEToggleAction *ta = (TDEToggleAction *) quantaApp->actionCollection()->action( "show_quanta_editor" );
   if (ta)
      ta->setChecked(true);

   //hide the VPL widget, reload the source if necessary
   if ((m_currentViewsLayout == SourceAndVPL && m_kafkaDocument->getKafkaWidget()->view()->hasFocus()) ||
     m_currentViewsLayout == VPLOnly)
   {
          reloadSourceView();
   }
   if (m_kafkaDocument->isLoaded())
       m_kafkaDocument->unloadDocument();

//show the document if full size
   m_splitter->hide();
   m_kafkaDocument->getKafkaWidget()->view()->reparent(0, 0, TQPoint(), false);
   m_document->view()->reparent(m_documentArea, 0, TQPoint(), true);
   m_document->view()->resize(m_documentArea->size());
   m_viewLayout->addWidget(m_documentArea, 1, 0);
   m_document->view()->setFocus();

   m_currentViewsLayout = SourceOnly;

//update timers are not needed in source only mode
   m_sourceUpdateTimer.stop();
   m_VPLUpdateTimer.stop();
}


void QuantaView::slotSetSourceAndVPLLayout()
{
   emit hidePreview();
   if (m_currentViewsLayout == SourceAndVPL  || !m_document)
     return;

   TDEToggleAction *ta = (TDEToggleAction *) quantaApp->actionCollection()->action( "show_kafka_and_quanta" );

   if (m_document->defaultDTD()->name.contains("HTML", false) == 0)
   {
      KMessageBox::information(this, i18n("The VPL Mode does not support the current DTD, at the moment: %1").arg(m_document->defaultDTD()->nickName));
      TDEToggleAction *ta2 = (TDEToggleAction *) quantaApp->actionCollection()->action( "show_quanta_editor" );
      if (ta2)
          ta2->setChecked(true);
      return;
  }

   if (ta)
       ta->setChecked(true);


    if (!m_kafkaDocument->isLoaded())
        m_kafkaDocument->loadDocument(m_document);
    if (m_currentViewsLayout == VPLOnly)
    {
      reloadSourceView();
    }
    m_kafkaDocument->getKafkaWidget()->view()->reparent(m_splitter, 0, TQPoint(), true);
    m_splitter->moveToFirst(m_kafkaDocument->getKafkaWidget()->view());
    m_document->view()->reparent(m_splitter, 0, TQPoint(), true);
    m_viewLayout->addWidget(m_splitter, 1, 0);
    m_splitter->setSizes(m_splitterSizes);
    m_splitter->show();

    if ( m_currentViewsLayout == SourceOnly &&
         (!baseNode || (baseNode->tag->type == Tag::Empty &&
          !baseNode->next && !baseNode->child)))
    {
        quantaApp->documentProperties(true);
    }

   m_currentViewsLayout = SourceAndVPL;

   reloadUpdateTimers();
}

void QuantaView::slotSetVPLOnlyLayout()
{
   emit hidePreview();
   if (m_currentViewsLayout == VPLOnly || !m_document)
     return;

   if(m_currentViewsLayout == SourceAndVPL)
     m_splitterSizes = m_splitter->sizes();

   TDEToggleAction *ta = (TDEToggleAction *) quantaApp->actionCollection()->action( "show_kafka_view" );

   if (m_document->defaultDTD()->name.contains("HTML", false) == 0)
   {
      KMessageBox::information(this, i18n("The VPL Mode does not support the current DTD, at the moment: %1").arg(m_document->defaultDTD()->nickName));
      TDEToggleAction *ta2 = (TDEToggleAction *) quantaApp->actionCollection()->action( "show_quanta_editor" );
      if (ta2)
          ta2->setChecked(true);
      return;
  }

   if (ta)
       ta->setChecked(true);

   m_splitter->hide();
   if (!m_kafkaDocument->isLoaded())
        m_kafkaDocument->loadDocument(m_document);

   m_kafkaDocument->getKafkaWidget()->view()->reparent(m_documentArea, 0, TQPoint(), true);
   m_kafkaDocument->getKafkaWidget()->view()->resize(m_documentArea->size());
   m_viewLayout->addWidget(m_documentArea, 1, 0);
   m_kafkaDocument->getKafkaWidget()->view()->setFocus();

   if ( m_currentViewsLayout == SourceOnly &&
         (!baseNode || (baseNode->tag->type == Tag::Empty &&
          !baseNode->next && !baseNode->child)))
   {
      quantaApp->documentProperties(true);
   }

   m_currentViewsLayout = VPLOnly;


//update timers are not needed in VPL only mode
    m_sourceUpdateTimer.stop();
    m_VPLUpdateTimer.stop();
}

void QuantaView::reloadUpdateTimers()
{
    QuantaView* view=ViewManager::ref()->activeView();

    m_sourceUpdateTimer.stop();
    m_VPLUpdateTimer.stop();

   if (m_kafkaDocument->isLoaded() && m_currentViewsLayout == SourceAndVPL && view && view == this)
   {
       if (m_currentFocus == VPLFocus && !qConfig.quantaRefreshOnFocus)
          m_sourceUpdateTimer.start(qConfig.quantaRefreshDelay);
       if (m_currentFocus == SourceFocus && !qConfig.kafkaRefreshOnFocus)
          m_VPLUpdateTimer.start(qConfig.kafkaRefreshDelay);
   }
}

void QuantaView::slotVPLGetFocus(bool focus)
{
  // is Quanta exiting?
  if (!quantaApp) return;
#ifdef LIGHT_DEBUG
  kdDebug(25001)<< "slotVPLGetFocus(" << focus << ")" << endl;
#endif
  int contentsX, contentsY;
  TDEAction *action;

  if(focus)
  {
    //We reload the kafka part from the Node Tree
    if (m_currentViewsLayout == SourceAndVPL && m_currentFocus == SourceFocus)
    {

      contentsX = m_kafkaDocument->getKafkaWidget()->view()->contentsX();
      contentsY = m_kafkaDocument->getKafkaWidget()->view()->contentsY();

      //Reload the kafka Editor only if Quanta was modified or if something has happened (e.g. a reparse)
      //and NEED a kafka reload.
      if  (parser->parsingNeeded())
          baseNode = parser->rebuild(m_document);
      reloadVPLView();
      //doesn't work!
      m_kafkaDocument->getKafkaWidget()->view()->setContentsPos(contentsX, contentsY);
    }

    //We disable some actions which doesn't work on kafka for the moment
    action = quantaApp->actionCollection()->action("tag_edit_table");
    if(action)
      action->setEnabled(false);
    action = 0L;
    action = quantaApp->actionCollection()->action("tag_quick_list");
    if(action)
      action->setEnabled(false);
    action = 0L;
    action = quantaApp->actionCollection()->action("tag_color");
    if(action)
      action->setEnabled(false);
    action = 0L;
    action = quantaApp->actionCollection()->action("tag_mail");
    if(action)
      action->setEnabled(false);
    action = 0L;
    action = quantaApp->actionCollection()->action("tag_misc");
    if(action)
      action->setEnabled(false);
    action = 0L;
    action = quantaApp->actionCollection()->action("tag_frame_wizard");
    if(action)
      action->setEnabled(false);
    action = 0L;
    action = quantaApp->actionCollection()->action("insert_css");
    if(action)
      action->setEnabled(false);
    action = 0L;
    action = quantaApp->actionCollection()->action("insert_char");
    if(action)
      action->setEnabled(false);

   //TEMPORARY: Enable VPL undo/redo logging
   m_document->docUndoRedo->turnOn(true);

    m_currentFocus = VPLFocus;
    reloadUpdateTimers();
 }
}

void QuantaView::slotSourceGetFocus()
{
  // is Quanta exiting?
  if (!quantaApp) return;
#ifdef LIGHT_DEBUG
  kdDebug(25001)<< "slotSourceGetFocus(true)" << endl;
#endif
  TDEAction *action;

  quantaApp->partManager()->setActivePart(m_document->doc(), m_document->view());
  //We reload the quanta view from the Node Tree.
  if (m_currentViewsLayout == SourceAndVPL && m_currentFocus == VPLFocus)
  {
    reloadSourceView();

    //FIXME: the tree (and the output)is right, the pos aren't.
    //This will reparse the whole Node tree and reload kafka.
     baseNode = parser->parse(m_document);
  }

  m_currentFocus = SourceFocus;
  reloadUpdateTimers();

    //We enable some actions which doesn't work on kafka for the moment
    action = quantaApp->actionCollection()->action("tag_edit_table");
    if(action)
      action->setEnabled(true);
    action = 0L;
    action = quantaApp->actionCollection()->action("tag_quick_list");
    if(action)
      action->setEnabled(true);
    action = 0L;
    action = quantaApp->actionCollection()->action("tag_color");
    if(action)
      action->setEnabled(true);
    action = 0L;
    action = quantaApp->actionCollection()->action("tag_mail");
    if(action)
      action->setEnabled(true);
    action = 0L;
    action = quantaApp->actionCollection()->action("tag_misc");
    if(action)
      action->setEnabled(true);
    action = 0L;
    action = quantaApp->actionCollection()->action("tag_frame_wizard");
    if(action)
      action->setEnabled(true);
    action = 0L;
    action = quantaApp->actionCollection()->action("insert_css");
    if(action)
      action->setEnabled(true);
    action = 0L;
    action = quantaApp->actionCollection()->action("insert_char");
    if(action)
      action->setEnabled(true);

    //TEMPORARY: Disable VPL undo/redo logging
    m_document->docUndoRedo->turnOn(false);

}

/** Reloads both views ONLY when changes have been made to the Node tree ONLY. */
void QuantaView::reloadBothViews(bool force)
{
  reloadSourceView(force);
  reloadVPLView(force);
}

/** reload the Kafka view from the Node Tree. Set force to true if you want to reload even if not necessary. */
void QuantaView::reloadVPLView(bool force)
{
  if (m_document && (m_kafkaReloadingEnabled || force))
      m_document->docUndoRedo->reloadKafkaEditor(force);
}

/** reload the Quanta view from the Node Tree. Set force to true if you want to reload even if not necessary. */
void QuantaView::reloadSourceView(bool force)
{
  if (m_quantaReloadingEnabled || force)
      m_document->docUndoRedo->reloadQuantaEditor(force);
}


void QuantaView::VPLUpdateTimerTimeout()
{
  if(quantaApp && m_currentFocus == SourceFocus)
    reloadVPLView();
}

void QuantaView::sourceUpdateTimerTimeout()
{
  if(quantaApp && m_currentFocus == VPLFocus)
    reloadSourceView();
}

void QuantaView::slotVPLLoadingError(Node *)
{
   emit showProblemsView();
}


void QuantaView::slotSetCursorPositionInSource(int col, int line)
{
  m_curCol = col;
  m_curLine = line;
  if (m_currentViewsLayout == SourceAndVPL || m_currentViewsLayout == SourceOnly)
    m_document->viewCursorIf->setCursorPositionReal(line, col);
}

void QuantaView::dragEnterEvent(TQDragEnterEvent *e)
{
  e->accept(KURLDrag::canDecode(e));
}

void QuantaView::dropEvent(TQDropEvent *e)
{
   emit dragInsert(e);
}

void QuantaView::resizeEvent(TQResizeEvent *e)
{
  TQWidget::resizeEvent(e);
  resize(m_documentArea->width(), m_documentArea->height());
}

void QuantaView::resize(int width, int height)
{
  if (m_plugin && m_plugin->widget())
  {
      m_plugin->widget()->resize(width, height);
      return;
  } else
  if (m_customWidget)
  {
      m_customWidget->resize(width, height);
      return;
  } else
  if (!m_document)
      return;
  if (m_currentViewsLayout == SourceOnly)
    m_document->view()->resize(width, height);
  else
  if (m_currentViewsLayout == VPLOnly)
    m_kafkaDocument->getKafkaWidget()->view()->resize(width,height);
  else
  if (m_currentViewsLayout == SourceAndVPL)
  {
    m_splitter->resize(width, height);
    m_splitterSizes = m_splitter->sizes();
  }
}

void QuantaView::insertTag(const char *tag)
{
   if (!m_document )
      return;
  TQString tagStr = QuantaCommon::tagCase(tag);
  const DTDStruct *dtd = m_document->currentDTD(true);
  bool single = QuantaCommon::isSingleTag(dtd->name, tagStr);
  bool optional = QuantaCommon::isOptionalTag(dtd->name, tagStr);

  TQString startTag = tagStr;
  startTag.prepend("<");
  if ( dtd->singleTagStyle == "xml" &&
       ( single || (optional && !qConfig.closeOptionalTags))
     )
  {
    startTag.append(" /");
  }
  startTag.append(">");

  if ( (qConfig.closeTags && !single && !optional) ||
       (qConfig.closeOptionalTags && optional) )
  {
     m_document->insertTag( startTag, TQString("</")+tagStr+">");
  }
  else
  {
     m_document->insertTag(startTag);
  }
}

//FIXME: Move out from here??
/** Insert a new tag by bringing up the TagDialog. */
void QuantaView::insertNewTag(const TQString &tag, const TQString &attr, bool insertInLine)
{
  if (m_document)
  {
    if (m_currentFocus == QuantaView::VPLFocus ||
        (m_currentFocus == QuantaView::SourceFocus && qConfig.smartTagInsertion))
      insertOutputInTheNodeTree("", "", quantaApp->showTagDialogAndReturnNode(tag, attr));
    else
    {
      TQString selection;
      if (m_document->selectionIf)
        selection = m_document->selectionIf->selection();
      TagDialog *dlg = new TagDialog(QuantaCommon::tagFromDTD(m_document->getDTDIdentifier(), tag), selection, attr, baseURL());
      if (dlg->exec())
      {
        dlg->insertTag(m_document, insertInLine);
      }

      delete dlg;
    }
  }
}

void QuantaView::insertOutputInTheNodeTree(const TQString &str1, const TQString &str2, Node *node)
{
  if (!m_document)
    return;
#ifdef LIGHT_DEBUG
  if (node)
    kdDebug(25001)<< "QuantaView::insertOutputInTheNodeTree() - node : " << node->tag->name <<
      " - type : " << node->tag->type << endl;
  else
    kdDebug(25001)<< "QuantaView::insertOutputInTheNodeTree() - str1 : " << str1 <<
                " - str2 : " << str2 << endl;
#endif
  KafkaWidget *kafkaPart = m_kafkaDocument->getKafkaWidget();
  NodeModifsSet *modifs;
  DOM::Node domNode, domStartContainer, domEndContainer;
  TQString tagName;
  TQTag *nodeTQTag, *qTag, *nodeParentTQTag;
  Node *nodeCursor, *startContainer, *endContainer, *nodeParent, *dummy;
  TQPtrList<TQTag> qTagList;
  int startCol, startLine, endCol, endLine;
  bool specialTagInsertion = false;
  long nodeOffset, startOffset, endOffset, domNodeOffset;
  TQValueList<int> loc;
  uint line, col;
  bool smartTagInsertion, hasSelection, nodeTreeModified;

  if (!node && str1.isEmpty() || node && !str1.isEmpty())
      return;

        //Three cases :
        //- Tag insertion in VPL
        //- Normal tag insertion in kate
        //- Smart tag insertion in kate
  smartTagInsertion = (m_currentFocus == QuantaView::SourceFocus && qConfig.smartTagInsertion);

  if (m_currentFocus == QuantaView::VPLFocus || smartTagInsertion)
  {
    modifs = new NodeModifsSet();
    if (!node && !str1.isEmpty())
    {
      //We build the node from the str1
      node = kafkaCommon::createNode("", "", Tag::XmlTag, m_document);
      node->tag->parse(str1, m_document);
      node->tag->name = QuantaCommon::tagCase(node->tag->name);
      node->tag->single = QuantaCommon::isSingleTag(m_document->defaultDTD()->name,
              node->tag->name);
    }
    if (m_currentFocus == QuantaView::VPLFocus)
    {
      kafkaPart->getCurrentNode(domNode, domNodeOffset);
      nodeCursor = m_kafkaDocument->getNode(domNode);
    }
    else
    {
      m_document->viewCursorIf->cursorPositionReal(&line, &col);
      nodeCursor = parser->nodeAt(line, col, false);
    }

    if (!nodeCursor)
        return;

    nodeParent = nodeCursor;
    if (nodeParent->tag->type == Tag::Text)
        nodeParent = nodeParent->parent;

  //Checking if at least one parent of node can have a Text Node as child, otherwise
  //it is impossible for the
  //user to add this node. In that case, try to insert the Node in the closest parent accepting it.
  //e.g. TR : a normal insertion would require to have the caret in the TABLE Node, but it is
              //impossible
    nodeTQTag = QuantaCommon::tagFromDTD(m_document->defaultDTD(),
                                        node->tag->name);

    if (!nodeTQTag)
        return;

    qTagList = nodeTQTag->parents();
#ifdef HEAVY_DEBUG
    kdDebug(25001)<< "nodeTQTag name : " << nodeTQTag->name() << endl;
    /**kdDebug(25001)<< nodeTQTag->isChild("#text", false) << endl;
    kdDebug(25001)<< nodeTQTag->isChild("#text", true) << endl;*/
#endif
    for (qTag = qTagList.first(); qTag; qTag = qTagList.next())
    {
      if (qTag->isChild("#text", false))
          break;
      if (qTag == qTagList.getLast())
          specialTagInsertion = true;
    }

    if (m_currentFocus == QuantaView::VPLFocus)
    {
      m_kafkaDocument->translateKafkaIntoNodeCursorPosition(domNode, domNodeOffset, &dummy, nodeOffset);
      kafkaPart->selection(domStartContainer, startOffset, domEndContainer, endOffset);
      m_kafkaDocument->translateKafkaIntoNodeCursorPosition(domStartContainer, startOffset,
              &startContainer, startOffset);
      m_kafkaDocument->translateKafkaIntoNodeCursorPosition(domEndContainer, endOffset,
              &endContainer,endOffset);
      hasSelection = kafkaPart->hasSelection();
    }
      else
      if (m_document->selectionIfExt)
      {
        m_kafkaDocument->translateQuantaIntoNodeCursorPosition(line, col, &dummy, nodeOffset);
        startCol = m_document->selectionIfExt->selStartCol();
        startLine = m_document->selectionIfExt->selStartLine();
        endCol = m_document->selectionIfExt->selEndCol();
        endLine = m_document->selectionIfExt->selEndLine();
        m_kafkaDocument->translateQuantaIntoNodeCursorPosition((unsigned)startLine, (unsigned)startCol,
                &startContainer, startOffset);
        m_kafkaDocument->translateQuantaIntoNodeCursorPosition((unsigned)endLine, (unsigned)endCol,
                &endContainer, endOffset);
        hasSelection = m_document->selectionIf->hasSelection();
        if (startContainer == endContainer && startContainer->tag->type == Tag::Empty)
        {
          hasSelection = false;
        }
        if (endContainer && endContainer->tag->type == Tag::XmlTag && endOffset < (signed)endContainer->tag->tagStr().length())
        {
          endContainer = endContainer->previousSibling();
          endOffset = (endContainer)?endContainer->tag->tagStr().length():0;
        }
     /**else
        {
          if (startContainer && startContainer->tag->type == Tag::Empty)
            startContainer = startContainer->nextNE();
          if (endContainer && endContainer->tag->type == Tag::Empty)
            endContainer = endContainer->prevNE();
        }*/
      }

      nodeTreeModified = false;
      if (specialTagInsertion)
      {
        //let's try to insert this node in the closest parent accepting it.
        while (nodeParent)
        {
          nodeParentTQTag =
                  QuantaCommon::tagFromDTD(m_document->defaultDTD(),
                  nodeParent->tag->name);
          if (nodeParentTQTag && nodeParentTQTag->isChild(node))
          {
            nodeCursor = kafkaCommon::createMandatoryNodeSubtree(node,
                    m_document);
            nodeOffset = 0;
            kafkaCommon::insertNodeSubtree(node, nodeParent, 0L, 0L, modifs);
            nodeTreeModified = true;
            break;
          }
          nodeParent = nodeParent->parent;
        }
      }
      else if (hasSelection && !nodeTQTag->isSingle())
      {
        //If some text is selected in kafka, surround the selection with the new Node.
        if(!startContainer || !endContainer)
            return;
        nodeTreeModified = kafkaCommon::DTDinsertRemoveNode(node, startContainer, (int)startOffset,
                endContainer, (int)endOffset, m_document, &nodeCursor,
                nodeOffset, modifs);
      }
      else
      {
        //Nothing is selected, simply inserting the Node if it is not an inline.
/*            if(!kafkaCommon::isInline(node->tag->name) || nodeTQTag->isSingle())
          {*/
        nodeTreeModified = kafkaCommon::DTDinsertRemoveNode(node, nodeCursor, (int)nodeOffset, nodeCursor,
                (int)nodeOffset, m_document, &nodeCursor, nodeOffset, modifs);
//             }
      }

      m_document->docUndoRedo->addNewModifsSet(modifs, undoRedo::NodeTreeModif);
      if (m_currentFocus == QuantaView::VPLFocus)
      {
                //view->reloadVPLView();
                //Now update the VPL cursor position
        m_kafkaDocument->translateNodeIntoKafkaCursorPosition(nodeCursor, nodeOffset, domNode,
                domNodeOffset);
        if (!domNode.isNull() && domNode.nodeType() != DOM::Node::TEXT_NODE &&
            !domNode.firstChild().isNull() && domNode.firstChild().nodeType() ==
            DOM::Node::TEXT_NODE)
            domNode = domNode.firstChild();
        if (!domNode.isNull())
            kafkaPart->setCurrentNode(domNode, domNodeOffset);
      }
      else
      {
        //view->reloadSourceView();
        //Now update the source cursor position
        m_kafkaDocument->translateNodeIntoQuantaCursorPosition(nodeCursor, nodeOffset, line, col);
        m_document->viewCursorIf->setCursorPositionReal(line, col);
      }
      if (!nodeTreeModified)
          quantaApp->slotStatusMsg(i18n("Cannot insert the tag: invalid location."));

    }
    else
    {
      m_document->insertTag(str1, str2);
    }
}


/** Returns the baseURL of the document. */
KURL QuantaView::baseURL()
{
  KURL base;
  if (m_document && !m_document->isUntitled() )
  {
    base = QuantaCommon::convertToPath(m_document->url());
  } else
  {
    base = Project::ref()->projectBaseURL();
  }
  return base;
}

void QuantaView::refreshWindow()
{
  if (!m_document)
  {
    if (m_plugin)
       quantaApp->partManager()->setActivePart(m_plugin->part(), m_plugin->widget());
       resize(width(), height());
  } else
  {
/*
    kdDebug(24000) << "m_documentArea->height(): " << m_documentArea->height() << endl;
    kdDebug(24000) << "ToolbarTabWidget::ref()->height(): " << ToolbarTabWidget::ref()->height() << " hidden: " << ToolbarTabWidget::ref()->isHidden() << " visible: " << ToolbarTabWidget::ref()->isVisible() << endl;
    kdDebug(24000) <<"sum: " << m_documentArea->height() + ToolbarTabWidget::ref()->height() << endl;
    kdDebug(24000) << "height(): " << height() << endl;
    */
    if (m_documentArea->height() + ToolbarTabWidget::ref()->height() - 1 > height() && !ToolbarTabWidget::ref()->isHidden()) //don't use isVisible alone instead of isHidden!
        resize(m_documentArea->width(), m_documentArea->height() - ToolbarTabWidget::ref()->height());
      else if (ToolbarTabWidget::ref()->isHidden())
        resize(width(), height());
  }
}

void QuantaView::activated()
{
  if (!m_document)
  {
    parser->setSAParserEnabled(false);
    quantaApp->slotReloadStructTreeView();
    refreshWindow();
    return;
  }
  ToolbarTabWidget::ref()->reparent(this, 0, TQPoint(), qConfig.enableDTDToolbar);
  m_viewLayout->addWidget(ToolbarTabWidget::ref(), 0 , 0);
  quantaApp->partManager()->setActivePart(m_document->doc(), m_document->view());
  m_document->checkDirtyStatus();
  StructTreeView::ref()->useOpenLevelSetting = true;
  quantaApp->slotLoadToolbarForDTD(m_document->getDTDIdentifier());

  //TEMP : If the activated document is not a (X)HTML document, disable smartTagInsertion
  //Will be removed when VPL will support every DTD
  TDEAction *action = quantaApp->actionCollection()->action("smart_tag_insertion");
  if(action && m_document->defaultDTD()->name.contains("HTML", false) == 0)
  {
    qConfig.smartTagInsertion = false;
    (static_cast<TDEToggleAction* >(action))->setChecked(false);
  }

  reloadLayout();
  refreshWindow();
 }


void QuantaView::deactivated()
{
  if (m_plugin)
  {
    quantaApp->statusBar()->changeItem("", IDS_STATUS);
  }
  m_sourceUpdateTimer.stop();
  m_VPLUpdateTimer.stop();
}

bool QuantaView::saveModified(bool ask)
{
  if (!m_document)
    return true;

  bool completed=true;
  TQString fileName = m_document->url().fileName();

  if (m_document->isModified() )
  {
    if (m_currentFocus == VPLFocus)
      reloadSourceView();
    int want_save;
    if (ask)
      want_save = KMessageBox::warningYesNoCancel(this,
          i18n("The file \"%1\" has been modified.\nDo you want to save it?").arg(fileName),
          i18n("Warning"), KStdGuiItem::save(), KStdGuiItem::discard());
    else
      want_save = KMessageBox::Yes;

    switch (want_save)
    {
      case KMessageBox::Yes :
           if (m_document->isUntitled())
           {
             completed = quantaApp->slotFileSaveAs(this);
           }
           else
           {
             completed = saveDocument(m_document->url());
           };

           break;

      case KMessageBox::No :
           {
      	     m_document->removeBackup(quantaApp->config());
      	     completed=true;
	   }
           break;

      case KMessageBox::Cancel :
           completed=false;
           break;

      default:
           completed=false;
           break;
    }
  } else
    m_document->removeBackup(quantaApp->config());
  return completed;
}

bool QuantaView::saveDocument(const KURL& url)
{
  if (url.isEmpty())
    return false;

  emit eventHappened("before_save", url.url(), TQString());
  m_saveResult = true;
  KURL oldURL = m_document->url();
  if (!m_document->isUntitled() && oldURL.isLocalFile())
  {
    fileWatcher->removeFile(oldURL.path());
//    kdDebug(24000) << "removeFile[saveDocument]: " << oldURL.path() << endl;
  }
  if (url.isLocalFile())
  {
    if (!m_document->saveAs(url))
    {
      fileWatcher->addFile(oldURL.path());
//      kdDebug(24000) << "addFile[saveDocument]: " << oldURL.path() << endl;
      return false; //saving to a local file failed
    } else //successful saving to a local file
    {
      m_document->setDirtyStatus(false);
      m_document->removeBackup(quantaApp->config());
      fileWatcher->addFile(m_document->url().path());
//      kdDebug(24000) << "addFile[saveDocument, 2]: " << m_document->url().path() << endl;
    }
  } else //saving to a remote file
  {
    KTextEditor::Document *doc = m_document->doc();
    m_eventLoopStarted = false;
    connect(doc, TQ_SIGNAL(canceled(const TQString &)), this, TQ_SLOT(slotSavingFailed(const TQString &)));
    connect(doc, TQ_SIGNAL(completed()), this, TQ_SLOT(slotSavingCompleted()));
    m_saveResult = m_document->saveAs(url);
    if (m_saveResult)
    {
      //start an event loop and wait until the saving finished
      QExtFileInfo internalFileInfo;
      m_eventLoopStarted = true;
      internalFileInfo.enter_loop();
    }
    disconnect(doc, TQ_SIGNAL(canceled(const TQString &)), this, TQ_SLOT(slotSavingFailed(const TQString &)));
    disconnect(doc, TQ_SIGNAL(completed()), this, TQ_SLOT(slotSavingCompleted()));
    if (!m_saveResult) //there was an error while saving
    {
      if (oldURL.isLocalFile())
      {
        fileWatcher->addFile(oldURL.path());
//        kdDebug(24000) << "addFile[saveDocument, 3]: " << oldURL.path() << endl;
      }
      return false;
    }
  }
  // everything went fine
  if (oldURL != m_document->url())
  {
      setCaption(m_document->url().fileName());
  }
  emit eventHappened("after_save", m_document->url().url(), TQString());
  return true;
}

void QuantaView::slotSavingFailed(const TQString &error)
{
  Q_UNUSED(error);
  m_saveResult = false;
  if (m_eventLoopStarted)
    tqApp->exit_loop();
}

void QuantaView::slotSavingCompleted()
{
  m_saveResult = true;
  m_document->setDirtyStatus(false);
  m_document->removeBackup(quantaApp->config());
  if (m_eventLoopStarted)
    tqApp->exit_loop();
}

#include "quantaview.moc"
