/***************************************************************************
                          quantadoc.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-2003 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 <tqdir.h>
#include <tqfileinfo.h>
#include <tqwidget.h>
#include <tqtabwidget.h>
#include <tqtabbar.h>
#include <tqlayout.h>
#include <tqdragobject.h>
#include <tqobject.h>


// include files for KDE
#include <tdelocale.h>
#include <tdeaction.h>
#include <tdeconfig.h>
#include <tdepopupmenu.h>
#include <tdemessagebox.h>
#include <kdirwatch.h>
#include <tdeversion.h>
#include <kdebug.h>
#include <kiconloader.h>

#include <tdetexteditor/view.h>
#include <tdetexteditor/configinterface.h>
#include <tdetexteditor/highlightinginterface.h>
#include <tdetexteditor/popupmenuinterface.h>
#include <tdetexteditor/markinterface.h>
#include <tdetexteditor/undointerface.h>
#include <tdetexteditor/viewcursorinterface.h>
#include <tdetexteditor/clipboardinterface.h>
#include <tdetexteditor/selectioninterface.h>
#include <tdetexteditor/encodinginterface.h>
#include <tdetexteditor/dynwordwrapinterface.h>
#include <tdetexteditor/editorchooser.h>
#include <tdetexteditor/editinterface.h>

#include <tdeparts/componentfactory.h>

#include <klibloader.h>
#include <ktrader.h>


// application specific includes
#include "document.h"
#include "quanta.h"
#include "quantadoc.h"
#include "quantaview.h"
#include "viewmanager.h"

#include "quantacommon.h"
#include "qextfileinfo.h"
#include "resource.h"
#include "debuggermanager.h"

#include "project.h"
//#include "dtds.h"

#include "undoredo.h"
#include "tagactionmanager.h"
#include "tagactionset.h"

QuantaDoc::QuantaDoc(TQWidget *parent, const char *name) : TQObject(parent, name)
{
  fileWatcher = new KDirWatch(this);

  attribMenu = new TDEPopupMenu();
  attribMenu->insertTitle(i18n("Tag"));
  connect( attribMenu, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotInsertAttrib(int)));
}

QuantaDoc::~QuantaDoc()
{
  //kdDebug(24000) << "QuantaDoc::~QuantaDoc: " << endl;
}


bool QuantaDoc::newDocument( const KURL& url, bool switchToExisting )
{
  bool newfile = false;
  if ( url.url().isEmpty() ) newfile = true;
  Document *w = 0L;
  QuantaView *view = ViewManager::ref()->isOpened(url);
  if (!view || newfile)
  {
    w = ViewManager::ref()->activeDocument();
    if (w && !newfile && !w->isModified() && w->isUntitled() && !w->busy)
         ViewManager::ref()->removeActiveView(false);

/*    
    // no modi and new -> we can remove                           !!!!
    w = ViewManager::ref()->activeDocument();
    if (w && !w->isModified() &&
         w->isUntitled() && !w->busy)
    {
      //workaround for some strange Katepart behavior. If there is a highlighting mode
      //selected and new content is loaded, the highlighting is reset to None. To avoid this
      //remove the untitled document and create a new one, where we don't set the
      //highlighting
      ViewManager::ref()->removeActiveView(false);
      w = ViewManager::ref()->activeDocument();
   
      if (w && !w->isModified() && w->isUntitled() && !w->busy)
          return true;
    }
*/
    // now we can create new kwrite
    ViewManager::ref()->createNewDocument();
    view = ViewManager::ref()->activeView();

    if (Project::ref()->hasProject())
        view->document()->processDTD(Project::ref()->defaultDTD());
    else
        view->document()->processDTD(qConfig.defaultDocType);
  }
  else // select opened
  if (switchToExisting)
  {
    view->document()->checkDirtyStatus();
    if (view != ViewManager::ref()->activeView())
    {
      view->activate();
      view->activated();
    }
    return false; // don't need loadURL
  }

  return true;
}

void QuantaDoc::openDocument(const KURL& urlToOpen, const TQString &a_encoding,
                             bool switchToExisting, bool readOnly)
{
  bool idleTimerStatus = quantaApp->slotEnableIdleTimer(false);
  KURL url = urlToOpen;
  if (url.isLocalFile())
  {
    TQString path = TQDir(url.path()).canonicalPath();
    if (!path.isEmpty())
        url.setPath(path);
  }
  TQString encoding = a_encoding;
  if (!newDocument(url, switchToExisting))
  {
     quantaApp->slotEnableIdleTimer(idleTimerStatus);
     return;
  }
  Document *w = ViewManager::ref()->activeDocument();
  if (readOnly)
  {
    //might work only with Kate part
    TDEAction *writeLockAction =  w->view()->actionCollection()->action("tools_toggle_write_lock");
    if (writeLockAction)
      writeLockAction->activate();
  }
  if (!url.isEmpty())
  {
    if (QExtFileInfo::exists(url, true, quantaApp))
    {
       if (encoding.isEmpty())
          encoding = quantaApp->defaultEncoding();
       w->disconnect(TQT_SIGNAL(openingFailed(const KURL&)));
       connect(w, TQT_SIGNAL(openingFailed(const KURL&)), this, TQT_SLOT(slotOpeningFailed(const KURL&)));
       w->disconnect(TQT_SIGNAL(openingCompleted(const KURL&)));
       connect(w, TQT_SIGNAL(openingCompleted(const KURL&)), this, TQT_SLOT(slotOpeningCompleted(const KURL&)));
       w->open(url, encoding);
       quantaApp->setTitle(url.prettyURL(0, KURL::StripFileProtocol));
    }
    else
    {
      slotOpeningFailed(url);
    }
  } else
  {
    quantaApp->reparse(true);
    KTextEditor::HighlightingInterface* highlightIf = dynamic_cast<KTextEditor::HighlightingInterface*>(w->doc());
    if (highlightIf)
    {
      TQString hlName;
      int htmlIdx = -1, xmlIdx = -1;
      for (uint i = 0; i < highlightIf->hlModeCount(); i++)
      {
          hlName = highlightIf->hlModeName(i);
          if (hlName == "HTML")
            htmlIdx = i;
          if (hlName == "XML")
            xmlIdx = i;
      }
      const DTDStruct *dtd = w->defaultDTD();
      if (dtd->family == 1)
      {
          if (dtd->singleTagStyle == "xml")
            highlightIf->setHlMode(xmlIdx);
          else
            highlightIf->setHlMode(htmlIdx);
      }
    }
    emit newStatus();
  }
  quantaApp->slotEnableIdleTimer(idleTimerStatus);
}

void QuantaDoc::slotOpeningFailed(const KURL &url)
{
    Q_UNUSED(url);
    bool signalStatus = signalsBlocked();
    blockSignals(false);
    emit hideSplash();
    //Seems to be not needed anymore since KDE 3.2, but keep until it's completely verified
/*
    KMessageBox::error(quantaApp, i18n("<qt>Cannot open document <b>%1</b>.</qt>").arg(url.prettyURL(0, KURL::StripFileProtocol)));
*/
    ViewManager::ref()->removeActiveView();
    blockSignals(signalStatus);
}

void QuantaDoc::slotOpeningCompleted(const KURL &url)
{
  Document *w = ViewManager::ref()->activeDocument();
  Project::ref()->loadBookmarks(w->url(), dynamic_cast<KTextEditor::MarkInterface*>(w->doc()));
    
  quantaApp->fileRecent->addURL(url);
  quantaApp->slotRepaintPreview();
  quantaApp->reparse(true);
  if (url.isLocalFile())
    quantaApp->debugger()->fileOpened(url.prettyURL(0, KURL::StripFileProtocol));
  quantaApp->slotNewStatus();
  quantaApp->setTitle(url.prettyURL(0, KURL::StripFileProtocol));
  Project::ref()->loadCursorPosition(w->url(), dynamic_cast<KTextEditor::ViewCursorInterface*>(w->view()));
  emit eventHappened("after_open", url.url(), TQString());
  
  bool flag = TagActionManager::canIndentDTD(w->defaultDTD()->name);
  quantaApp->actionCollection()->action("apply_source_indentation")->setEnabled(flag);
}

/** show popup menu with list of attributes for current tag */
void QuantaDoc::slotAttribPopup()
{
  Document *w = ViewManager::ref()->activeDocument();
  if (!w)  return;

  attribMenu->clear();
  uint line, col;
  w->viewCursorIf->cursorPositionReal(&line, &col);

  Node *node = parser->nodeAt(line, col, false);
  if (node && node->tag)
  {
    Tag *tag = node->tag;
    TQString tagName = tag->name;
    TQStrIList attrList = TQStrIList();
    TQString name;

    for (int i=0; i < tag->attrCount(); i++ )
        attrList.append( tag->attribute(i).ascii() );

    if ( QuantaCommon::isKnownTag(w->getDTDIdentifier(),tagName) )
    {
      TQString caption = i18n("Attributes of <%1>").arg(tagName);
      attribMenu->insertTitle( caption );

      AttributeList *list = QuantaCommon::tagAttributes(w->getDTDIdentifier(),tagName );
      uint menuId = 0;
      for ( uint i = 0; i < list->count(); i++ )
      {
        name = list->at(i)->name;
        attribMenu->insertItem( name , i);//list->findIndex(*item) );
        if (attrList.contains(name.ascii()))
        {
          attribMenu->setItemEnabled( i , false );
        }
        menuId++;
      }

      TQTag* qtag = QuantaCommon::tagFromDTD(w->getDTDIdentifier(), tagName);
      for (TQStringList::Iterator it = qtag->commonGroups.begin(); it != qtag->commonGroups.end(); ++it)
      {
       TQPopupMenu* popUpMenu = new TQPopupMenu(attribMenu, (*it).latin1());
       AttributeList *attrs = qtag->parentDTD->commonAttrs->find(*it);
       for (uint j = 0; j < attrs->count(); j++)
       {
        name = attrs->at(j)->name;
        popUpMenu->insertItem(name, ++menuId);
        if (attrList.contains(name.ascii()))
        {
          popUpMenu->setItemEnabled( menuId , false );
        }
       }
       connect( popUpMenu, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotInsertAttrib(int)));
       attribMenu->insertItem(*it, popUpMenu);
      }

      if (menuId > 0)    // don't show empty menu, may be core dumped
      {
        attribMenu->setActiveItem( 0);

        TQPoint globalPos = w->view()->mapToGlobal(w->viewCursorIf->cursorCoordinates());
        TQFont font = w->view()->font();
        globalPos.setY(globalPos.y() + TQFontMetrics(font).height());
        attribMenu->exec(globalPos);
      }
    }
    else {
      TQString message = i18n("Unknown tag: %1").arg(tagName);
      quantaApp->slotStatusMsg( message );
    }
  }
}

void QuantaDoc::slotInsertAttrib( int id )
{
  Document *w = ViewManager::ref()->activeDocument();
  if (!w)  return;
  uint line, col;
  w->viewCursorIf->cursorPositionReal(&line, &col);
  Node *node = parser->nodeAt(line, col);
  if (node && node->tag)
  {
    Tag *tag = node->tag;
    TQString tagName = tag->name;
    if ( QuantaCommon::isKnownTag(w->getDTDIdentifier(), tagName) )
    {
      int menuId;
      AttributeList *list = QuantaCommon::tagAttributes(w->getDTDIdentifier(), tagName);
      menuId = list->count();
      TQString attrStr;
      if (id <= menuId)
      {
        attrStr = list->at(id)->name;
      } else
      {
        TQTag* qtag = QuantaCommon::tagFromDTD(w->getDTDIdentifier(), tagName);
        for (TQStringList::Iterator it = qtag->commonGroups.begin(); it != qtag->commonGroups.end(); ++it)
        {
          AttributeList *attrs = qtag->parentDTD->commonAttrs->find(*it);
          menuId += attrs->count();
          if (id <= menuId)
          {
            attrStr = attrs->at(id - (menuId - attrs->count()) -1)->name;
            break;
          }
        }
      }
      //now insert the new attribute into the tag
      int el, ec;
      tag->endPos(el, ec);
      w->viewCursorIf->setCursorPositionReal( el, ec );
      w->insertTag( " " + QuantaCommon::attrCase(attrStr) + "="+qConfig.attrValueQuotation, qConfig.attrValueQuotation );
    }

    delete attribMenu;
    attribMenu = new TDEPopupMenu();
    attribMenu->insertTitle(i18n("Tag"));
    connect( attribMenu, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotInsertAttrib(int)));
  }
}


#include "quantadoc.moc"
