/***************************************************************************
                          cssselector.cpp  -  description
                             -------------------
    begin                : mer ago 6 2003
    copyright            : (C) 2003 by gulmini luciano
    email                : gulmini.luciano@student.unife.it
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 "cssselector.h"
#include "csseditor.h"

#include <tqlineedit.h>
#include <tqcombobox.h>
#include <tqpushbutton.h>
#include <tqtextstream.h>
#include <tqtabwidget.h>
#include <tqobjectlist.h>
#include <tqfileinfo.h>
#include <tqlabel.h>
#include <tqregexp.h>
#include <tqwhatsthis.h>

#include <kstandarddirs.h>
#include <tdelocale.h>
#include <kdebug.h>
#include <tdemessagebox.h>
#include <kurlrequester.h>
#include <tdefiledialog.h>
#include "project.h"
#include "stylesheetparser.h"


CSSSelector::CSSSelector(TQWidget *parent, const char* name) : CSSSelectorS (parent,name),m_orderNumber(0),m_stopProcessingStylesheet(false) {

  m_currentItem = 0L;

  Connect();

  kurApplyToFile->fileDialog()->setURL(Project::ref()->projectBaseURL().url());
  kurApplyToFile->fileDialog()->setFilter( "*.html *.htm |" + i18n("HTML Files") +" (*.html *.htm)\n*.xhtml |" + i18n("XHTML Files")+" (*.xhtml)\n*.xml |" + i18n("XML Files")+" (*.xml)\n*.*|" + i18n("All Files")+" (*.*)"  );
  TQWhatsThis::add((TQLineEdit*)(kurApplyToFile->lineEdit()),"With this line edit you can insert the URL of the file you want to use to preview the style sheet you are editing");

  /*TQString configDir = locate("appdata", "csseditor/config.xml");
  configDir = TQFileInfo(configDir).dirPath() + "/";*/

  TQString configDir = TQFileInfo( locate("appdata", "csseditor/config.xml") ).dirPath() + "/";

  TQDomDocument doc;
  TQFile file( configDir+"pseudo.xml" );
  if ( !file.open( IO_ReadOnly ) )
    return;
  if ( !doc.setContent( &file ) ) {
    file.close();
    return;
  }
  file.close();

  TQDomElement docElem = doc.documentElement();

  TQDomNode n = docElem.firstChild();
  while( !n.isNull() ) {
    TQDomElement e = n.toElement();
    if( !e.isNull() ) {
      cbPseudo->insertItem(e.attribute("name"));
    }
    n = n.nextSibling();
  }

  file.setName( configDir+"dtdTags.xml" );
  if ( !file.open( IO_ReadOnly ) )
    return;
  if ( !doc.setContent( &file ) ) {
    file.close();
    return;
  }
  file.close();

 TQStringList dtdNames,
                    dtdNickNames;
 docElem = doc.documentElement();
 n = docElem.firstChild();
  while( !n.isNull() ) {
    TQDomElement e = n.toElement();
    if( !e.isNull() ) {
      dtdNames.append(e.attribute("name"));
      dtdNickNames.append(e.attribute("nickName"));
      if(e.attribute("default") == "yes") {
        TQStringList tagList = TQStringList::split( ',',e.text() );
        tagList.sort();
        cbTag->insertStringList( tagList );
        cbTag->setAutoCompletion(true);
      }
    }
    n = n.nextSibling();
  }
  cbDTD->insertStringList( dtdNickNames );
}

CSSSelector::~CSSSelector(){
}

void CSSSelector::Connect(){

  connect(cbDTD,TQ_SIGNAL(activated(const TQString&)),this,TQ_SLOT(setDTDTags(const TQString&)));

  connect(pbAddTag,TQ_SIGNAL(clicked()), this ,TQ_SLOT(addTag()));
  connect(pbAddClass,TQ_SIGNAL(clicked()), this ,TQ_SLOT(addClass()));
  connect(pbAddID,TQ_SIGNAL(clicked()), this ,TQ_SLOT(addID()));
  connect(pbAddPseudo,TQ_SIGNAL(clicked()), this ,TQ_SLOT(addPseudo()));

  connect(lvTags, TQ_SIGNAL(doubleClicked( TQListViewItem *  )), this, TQ_SLOT(openCSSEditor(TQListViewItem *)));
  connect(lvClasses, TQ_SIGNAL(doubleClicked( TQListViewItem *  )), this, TQ_SLOT(openCSSEditor(TQListViewItem *)));
  connect(lvIDs, TQ_SIGNAL(doubleClicked( TQListViewItem *  )), this, TQ_SLOT(openCSSEditor(TQListViewItem *)));
  connect(lvPseudo, TQ_SIGNAL(doubleClicked( TQListViewItem *  )), this, TQ_SLOT(openCSSEditor(TQListViewItem *)));

  connect(lvTags, TQ_SIGNAL(selectionChanged( TQListViewItem *  )), this, TQ_SLOT(setCurrentItem(TQListViewItem *)));
  connect(lvClasses, TQ_SIGNAL(selectionChanged( TQListViewItem *  )), this, TQ_SLOT(setCurrentItem(TQListViewItem *)));
  connect(lvIDs, TQ_SIGNAL(selectionChanged( TQListViewItem *  )), this, TQ_SLOT(setCurrentItem(TQListViewItem *)));
  connect(lvPseudo, TQ_SIGNAL( selectionChanged( TQListViewItem *  )), this, TQ_SLOT(setCurrentItem(TQListViewItem *)));

  connect(pbRemoveSelectedTag,TQ_SIGNAL(clicked()), this ,TQ_SLOT(removeSelected()));
  connect(pbRemoveSelectedClass,TQ_SIGNAL(clicked()), this ,TQ_SLOT(removeSelected()));
  connect(pbRemoveSelectedID,TQ_SIGNAL(clicked()), this ,TQ_SLOT(removeSelected()));
  connect(pbRemoveSelectedPseudo,TQ_SIGNAL(clicked()), this ,TQ_SLOT(removeSelected()));

  connect(twSelectors,TQ_SIGNAL(currentChanged ( TQWidget * )), this ,TQ_SLOT(setCurrentListView( TQWidget * )));

  connect(pbRemoveAllTags,TQ_SIGNAL(clicked()), this ,TQ_SLOT(removeAll()));
  connect(pbRemoveAllClasses,TQ_SIGNAL(clicked()), this ,TQ_SLOT(removeAll()));
  connect(pbRemoveAllIDs,TQ_SIGNAL(clicked()), this ,TQ_SLOT(removeAll()));
  connect(pbRemoveAllPseudo,TQ_SIGNAL(clicked()), this ,TQ_SLOT(removeAll()));
}

void CSSSelector::setDTDTags(const TQString& s){

  TQString configDir = TQFileInfo( locate("appdata", "csseditor/config.xml") ).dirPath() + "/";

  TQDomDocument doc;

  TQFile file( configDir+"dtdTags.xml" );
  if ( !file.open( IO_ReadOnly ) )
    return;
  if ( !doc.setContent( &file ) ) {
    file.close();
    return;
  }
  file.close();

  TQStringList dtdNames;
  TQDomElement docElem = doc.documentElement();
  TQDomNode n = docElem.firstChild();
  while( !n.isNull() ) {
    if( n.toElement().attribute("nickName") == s )
     break;
  n = n.nextSibling();
  }
  TQStringList tagList = TQStringList::split( ',',n.toElement().text() );
  tagList.sort();
  cbTag->clear();
  cbTag->insertStringList( tagList );
  cbTag->setAutoCompletion(true);
}

void CSSSelector::addTag(){
  TQListViewItem *item = new TQListViewItem(lvTags);
  if(!cbTag->currentText().isEmpty()){
    item->setText(0,cbTag->currentText());
    TQPair<TQString, unsigned int> tmp(TQString(),++m_orderNumber);
    m_currentStylesheetStructure[item->text(0)]=tmp;
  }
}

void CSSSelector::addClass(){
  TQListViewItem *item = new TQListViewItem(lvClasses);
  if(!leClass->text().isEmpty()){
    item->setText(0,leClass->text());
    TQPair<TQString, unsigned int> tmp(TQString(),++m_orderNumber);
    m_currentStylesheetStructure[item->text(0)]=tmp;
  }
}

void CSSSelector::addID(){
  TQListViewItem *item = new TQListViewItem(lvIDs);
  if(!leID->text().isEmpty()){
    item->setText(0,leID->text());
    TQPair<TQString, unsigned int> tmp(TQString(),++m_orderNumber);
    m_currentStylesheetStructure[item->text(0)]=tmp;
  }
}

void CSSSelector::addPseudo(){
  TQListViewItem *item = new TQListViewItem(lvPseudo);
  item->setText(0,(lePseudoSelector->text()+":"+cbPseudo->currentText()).stripWhiteSpace());
  TQPair<TQString, unsigned int> tmp(TQString(),++m_orderNumber);
  m_currentStylesheetStructure[item->text(0)]=tmp;
}

void CSSSelector::openCSSEditor(TQListViewItem * i){
  if(!m_stopProcessingStylesheet){
    TQListView *lv = i->listView();
    TQListViewItem *temp;
    TQString s;
    TQObjectList *l = queryList( "TQListView" );
    TQObjectListIt it( *l ); // iterate over the listviews
    TQObject *obj;

    while ( (obj = it.current()) != 0 ) {
      TQListView *lvTemp = (TQListView*)obj;
      if( lv != lvTemp){
        temp = lvTemp->firstChild();
        while(temp){
          s+=(temp->text(0)+" { "+temp->text(1)+" } ");
          temp = temp->nextSibling();
        }
      }
      ++it;
    }
    delete l; // delete the list, not the objects

    temp = lv->firstChild();

    while(temp){
      if(temp != i) s+=(temp->text(0)+" { "+temp->text(1)+" } ");
      temp = temp->nextSibling();
   }

    CSSEditor dlg(i);
    if(m_callingFrom == "XHTML"){
      dlg.setHeader(m_header);
      dlg.setSelectors(s);
      dlg.setFooter(m_footer);
      dlg.setFileToPreview(m_fileToPreview,false);
    }
    else if(m_callingFrom == "CSS"){
              if(kurApplyToFile->url().isEmpty())
                dlg.hidePreviewer();
              else {
                dlg.setFileToPreview(kurApplyToFile->url(),true);

                TQString tmp;
                TQListViewItem *item = lvTags->firstChild();
                while( item ) {
                  if(i->text(0).stripWhiteSpace() != item->text(0).stripWhiteSpace())
                  tmp += item->text(0) + " {" + item->text(1) + "}";
                  item = item->nextSibling();
                }

                item = lvClasses->firstChild();
                while( item ) {
                  if(i->text(0).stripWhiteSpace() != item->text(0).stripWhiteSpace())
                  tmp += item->text(0) + " {" + item->text(1) + "}";
                  item = item->nextSibling();
                }

                item = lvIDs->firstChild();
                while( item ) {
                  if(i->text(0).stripWhiteSpace() != item->text(0).stripWhiteSpace())
                  tmp += item->text(0) + " {" + item->text(1) + "}";
                  item = item->nextSibling();
                }

                item = lvPseudo->firstChild();
                while( item ) {
                  if(i->text(0).stripWhiteSpace() != item->text(0).stripWhiteSpace())
                  tmp += item->text(0) + " {" + item->text(1) + "}";
                  item = item->nextSibling();
                }

                dlg.setExternalStyleSheetDefinition(tmp);
              }
   }

    dlg.initialize();

    if(dlg.exec())  {
      i->setText(1,dlg.generateProperties());
      TQPair<TQString, unsigned int> tmp(m_currentStylesheetStructure[i->text(0)]);
      tmp.first = dlg.generateProperties();
      m_currentStylesheetStructure[i->text(0)] = tmp;
    }
  }
}

void CSSSelector::setCurrentListView(TQWidget* w){
  TQObjectList *l = w->queryList( "TQListView" );
  m_currentListView = static_cast<TQListView*>(l->first());
}

void CSSSelector::removeAll(){
  TQListViewItemIterator it( m_currentListView );
        while ( it.current() ) {
            TQListViewItem *item = it.current();
            m_currentStylesheetStructure.remove(item->text(0));
            ++it;
        }
   m_currentListView->clear();
}

void CSSSelector::removeSelected(){
  if( m_currentItem ) {
    m_currentStylesheetStructure.remove(m_currentItem->text(0));
    delete m_currentItem;
    m_currentItem = 0L;
  }
}

void CSSSelector::loadCSSContent(const TQString& s){
  stylesheetParser p(s);
  connect(&p,TQ_SIGNAL(errorOccurred(const TQString&)), this, TQ_SLOT(setStylesheetProcessing(const TQString&)));
  p.parse();
  m_orderNumber = p.orderNumber();

  TQMap<TQString, TQPair<TQString,unsigned int> >::Iterator it;
  m_currentStylesheetStructure = p.stylesheetStructure();
  for ( it = m_currentStylesheetStructure.begin(); it != m_currentStylesheetStructure.end(); ++it ) {
    if(!it.key().startsWith("@rule") && !it.key().startsWith("/*")){
      TQListViewItem *item;
      if(it.key().contains(":")){
        item = new TQListViewItem(lvPseudo);
      }
      else
      if(it.key().contains("#")){
        item = new TQListViewItem(lvIDs);
      }
      else
      if(it.key().contains(".")){
        item = new TQListViewItem(lvClasses);
      }
      else {
        item = new TQListViewItem(lvTags);
      }

      item->setText(0,it.key());
      item->setText(1,it.data().first);

    }
  }
}

TQString CSSSelector::generateFormattedStyleSection(){
  TQMap< TQString,TQPair<TQString,unsigned int> >::Iterator it;
  TQString styleSection,tmpStr;
  unsigned int indentWidth,
                      indentDisplacement = 2;
  for ( unsigned int i=0;i<=m_orderNumber;i++ ) {
    for ( it = m_currentStylesheetStructure.begin(); it != m_currentStylesheetStructure.end(); ++it ) {
      TQString key = it.key();
      if(it.data().second == i){
       if(key.startsWith("@rule"))
         styleSection += it.data().first;
       else if(key.startsWith("/*"))
                 styleSection += it.data().first;
               else {
                  key.remove(TQRegExp("-v[\\d]+$"));
                  styleSection += "\n" + key + " {\n";
                  indentWidth = indentDisplacement + 2;
                  TQStringList props = TQStringList::split(";",it.data().first.simplifyWhiteSpace());
                  TQString indentStr;
                  indentStr.fill(' ',indentWidth);
                  for ( TQStringList::Iterator it = props.begin(); it != props.end(); ++it ) {
                    if((*it).startsWith(" "))
                      tmpStr +=  indentStr + (*it).remove(0,1) + ";\n";
                    else
                      tmpStr += indentStr + (*it) + ";\n";
                  }
                  indentStr.fill(' ', indentDisplacement);
                  styleSection += tmpStr + indentStr + "}\n\n";
                  tmpStr = TQString();
              }
      }
    }
  }
  return "\n"+styleSection;
}

void CSSSelector::enableApplyToFile(){
  tlApplyToFile->setEnabled(true);
  kurApplyToFile->setEnabled(true);
}

void CSSSelector::setStylesheetProcessing(const TQString& msg) {
  m_stopProcessingStylesheet=true;
  KMessageBox::error (0L, msg );
}

#include "cssselector.moc"
