/****************************************************************************
**
** Copyright (C) 1999-2006 Frank Hemer <frank@hemer.org>,
**                         Tilo Riemer <riemer@crossvc.com>,
**                         Wim Delvaux <wim.delvaux@chello.be>,
**                         Jose Hernandez <joseh@tesco.net>,
**                         Helmut Koll <HelmutKoll@web.de>,
**                         Tom Mishima <tmishima@mail.at-m.or.jp>,
**                         Joerg Preiss <auba@auba.de>
**
**
**----------------------------------------------------------------------------
**
**----------------------------------------------------------------------------
**
** CrossVC is available under two different licenses:
**
** If CrossVC is linked against the GPLed version of Qt 
** CrossVC is released under the terms of GPL also.
**
** If CrossVC is linked against a nonGPLed version of Qt 
** CrossVC is released under the terms of the 
** CrossVC License for non-Unix platforms (CLNU)
**
**
** CrossVC License for non-Unix platforms (CLNU):
**
** Redistribution and use in binary form, without modification, 
** are permitted provided that the following conditions are met:
**
** 1. Redistributions in binary form must reproduce the above copyright
**    notice, this list of conditions and the following disclaimer in the
**    documentation and/or other materials provided with the distribution.
** 2. It is not permitted to distribute the binary package under a name
**    different than CrossVC.
** 3. The name of the authors may not be used to endorse or promote
**    products derived from this software without specific prior written
**    permission.
** 4. The source code is the creative property of the authors.
**    Extensions and development under the terms of the Gnu Public License
**    are limited to the Unix platform. Any distribution or compilation of 
**    the source code against libraries licensed other than gpl requires 
**    the written permission of the authors.
**
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
** GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**
**
** CrossVC License for Unix platforms:
**
** 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, version 2 of the License.
** 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 version 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/

#include <qapplication.h>
#include <qdir.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qdatetime.h>
#include <qstringlist.h>
#include <qdict.h>
#include <qregexp.h>
#include <qsignal.h>
#include <qpainter.h>
#include <assert.h>

#include "config.h"
#include "directory.h"
#include "CvsDirectory.h"
#include "globals.h"
#include "filesubr.h"
#include "ccvscommand.h"
#include "cvslistview.h"
#include "noncvslistview.h"
#include "cvsignorelistview.h"
#include "pixmapcache.h"
#include "CCvsOutput.h"
#include "LinCVSLog.h"

#define DIRECTORY 123456

//----------------------------------------------------------------------------

void EntryState::setState( EntryStates newState ) {
   //   printf("\nfile: %s, state: %i, newstate: %i\n",m_fileName.ascii(),m_state,newState);
   if (m_state==newState) return;
   m_state = newState;

   if (newState>myDirectory->get_ES_Max()) myDirectory->set_ES_Max(newState);
   else if (newState<myDirectory->get_ES_Max()) myDirectory->calc_ES_Max();

   // bubble up
   myDirectory->setDirState( newState );
}

//============================================================================

const bool Directory::VIRTUAL = true;

GuardedDir Directory::s_activeDir = NULL;
QObject * Directory::s_control = NULL;
QListView * Directory::s_topView = NULL;
ColorTabWidget * Directory::s_pListView = NULL;
QPopupMenu * Directory::s_fileMenu = NULL;
CvsListView * Directory::s_FileListView = NULL;
NonCvsListView * Directory::s_NonFileListView = NULL;
CvsIgnoreListView * Directory::s_IgnoreFileListView = NULL;
//----------------------------------------------------------------------------

void Directory::initialize( const QObject * control,
      QListView * workBench,
      ColorTabWidget * listView,
      QPopupMenu * menu,
      CvsListView * fileListView,
      NonCvsListView  * nonFileListView,
      CvsIgnoreListView * ignoreFileListView) {
   s_control = const_cast<QObject *>(control);
   s_topView = workBench;
   s_pListView = listView;
   s_fileMenu = menu;
   s_FileListView = fileListView;
   s_NonFileListView = nonFileListView;
   s_IgnoreFileListView = ignoreFileListView;
}

// helps creating fast hash-tables
const unsigned int Directory::nextPrime( unsigned int i) {
   switch( i) {
      case 0: return 19;
      case 19: return 41;
      case 41: return 79;
      case 79: return 157;
      case 157: return 311;
      case 311: return 619;
      case 619: return 1249;
      case 1249: return 2503;
      case 2503: return 4999;
      case 4999: return 9973;
      default: return i*2+1;
   }
}

//----------------------------------------------------------------------------

bool Directory::setEntryState(QString &fileName, EntryStates stateId) {
   EntryState *entry;
   if (!(entry = m_entries.find(fileName))) return false;
   entry->setState(stateId);
   if (Debug::g_pLog) {
      QString msg = m_fullName+" changing EntryState: ";
      msg += fileName;
      msg += ", state: "+QString::number(stateId);
      Debug::g_pLog->log(Debug::LL_GOSSIP_MONGER, msg);
   }
   return true;
}

//----------------------------------------------------------------------------

void Directory::removeEntry(QString &fileName) {
   m_entries.remove(fileName);
   if (Debug::g_pLog) {
      QString msg = m_fullName+" removing EntryState: ";
      msg += fileName;
      Debug::g_pLog->log(Debug::LL_GOSSIP_MONGER, msg);
   }
}

//----------------------------------------------------------------------------

void Directory::appendEntry(QString &key, EntryState *entry) {
   if (m_entries.size() <= m_entries.count()) m_entries.resize( nextPrime(m_entries.size()));
   m_entries.insert(key,entry);
   if (entry->state()>m_curStateMax) m_curStateMax=entry->state();
   if (Debug::g_pLog) {
      QString msg = m_fullName+" appending EntryState: ";
      msg += key;
      msg += ", state: "+QString::number(entry->state());
      Debug::g_pLog->log(Debug::LL_GOSSIP_MONGER, msg);
   }
}

//----------------------------------------------------------------------------

//top level dir
Directory::Directory(QStringList)
   : DirBase(s_topView),
     m_tabIdx(0),
     m_pFileListView(NULL),
     m_pNonFileListView(NULL),
     m_pIgnoreFileListView(NULL),
     m_subProjectList(NULL),
     m_lastHScrollBarValue(0),
     m_lastVScrollBarValue(0)
{
   m_topDir = this;
   m_isSubProjectRoot = true;
   m_virtual = false;
   m_pDirectorySuicide = new QSignal(this);
   m_pDirectorySuicide->connect(s_control,SLOT(slot_requestDirectorySuicide(const QVariant&)));
   m_pFileContentsChanged = new QSignal(this);
   m_pFileContentsChanged->connect(s_control,SLOT(setDirMenu()));
}

//----------------------------------------------------------------------------

//subdir
Directory::Directory( QListViewItem * parent, 
      bool virtualDir)
   : DirBase( parent ),
     m_tabIdx(0),
     m_pFileListView(NULL),
     m_pNonFileListView(NULL),
     m_pIgnoreFileListView(NULL),
     m_subProjectList( NULL),
     m_pDirectorySuicide( NULL),
     m_lastHScrollBarValue(0),
     m_lastVScrollBarValue(0)
{
   m_pFileContentsChanged = new QSignal(this);
   m_pFileContentsChanged->connect(s_control,SLOT(setDirMenu()));
   m_topDir = dyncast_dir(dyncast_dir(parent)->topDir());
   m_isSubProjectRoot = false;
   m_virtual = virtualDir;
}

//----------------------------------------------------------------------------

Directory::~Directory() {
   releaseMonitoredDir(this);
}

//----------------------------------------------------------------------------

const int Directory::checkType(const QString path) {
   if (QFileInfo(path+"CVS").exists()) return Cvs;
   else return Plain;
}

//----------------------------------------------------------------------------

//top level dir
Directory * Directory::createInstance(const QString& fileName)
{
   QString path = fileName;
   if(path.length() > 1){//is not root
      path += "/";
   }
   switch(checkType(path)) {
      case Cvs: {
         return new CvsDirectory(NULL,fileName);
      }
      case Plain: {
	 Directory * dir = new Directory(NULL);
	 s_topView->setUpdatesEnabled(FALSE);
	 dir->init(fileName);
	 s_topView->setUpdatesEnabled(TRUE);
	 s_topView->triggerUpdate();
	 return dir;
      }
      default: {
	 assert(false);
      }
   }

   return 0; // makes the compiler happy
}

//----------------------------------------------------------------------------

//subdir
Directory * Directory::createChildInstance( QListViewItem * parent,
      const QString& fileName,
      bool virtualDir)
{
   QString path = fileName;
   if(path.length() > 1){//is not root
      path += "/";
   }
   switch(checkType(path)) {
      case Cvs: {
	 return new CvsDirectory(parent,fileName,virtualDir);
      }
      case Plain: {
	 Directory * dir = new Directory(parent,virtualDir);
	 bool update;
	 if (update = dir->s_topView->isUpdatesEnabled()) {
	    dir->s_topView->setUpdatesEnabled(FALSE);
	 }
	 dir->init(fileName);
	 if (update) {
	    dir->s_topView->setUpdatesEnabled(TRUE);
	    dir->s_topView->triggerUpdate();
	 }
	 return dir;
      }
      default: {
	 assert(false);
      }
   }

   return 0; // makes the compiler happy
}

//----------------------------------------------------------------------------

Directory * Directory::createChild( const QString& fileName, bool virtualDir) {
   Directory * dir = new Directory(this,virtualDir);
   bool update;
   if (update = dir->s_topView->isUpdatesEnabled()) {
      dir->s_topView->setUpdatesEnabled(FALSE);
   }
   dir->init(fileName);
   if (update) {
      dir->s_topView->setUpdatesEnabled(TRUE);
      dir->s_topView->triggerUpdate();
   }
   return dir;
}

//----------------------------------------------------------------------------

int Directory::compare ( QListViewItem * item, int row, bool ascending) const {
   int ret = QString::localeAwareCompare(text(row).lower(),item->text(row).lower());
   if (ret != 0) return ret;
   else return QListViewItem::compare(item,row,ascending);
}

//----------------------------------------------------------------------------

int Directory::rtti() const {
   return DIRECTORY;
}

//----------------------------------------------------------------------------

Directory * Directory::parent() const{
   if (QListViewItem::parent() && (QListViewItem::parent()->rtti() >= DIRECTORY)) {
      return static_cast<Directory*>(QListViewItem::parent());
   } else return NULL;
}

//----------------------------------------------------------------------------

Directory * Directory::firstChild() {
   QListViewItem * lvi = QListViewItem::firstChild();
   while ( lvi && (lvi->rtti() < DIRECTORY) ) {
      lvi = lvi->nextSibling();
   }
   return static_cast<Directory *>(lvi);
}

//----------------------------------------------------------------------------

Directory * Directory::nextSibling() {
   QListViewItem * lvi = QListViewItem::nextSibling();
   while ( lvi && (lvi->rtti() < DIRECTORY) ) {
      lvi = lvi->nextSibling();
   }
   return static_cast<Directory *>(lvi);
}
//----------------------------------------------------------------------------

Directory * Directory::dyncast_dir(QListViewItem * lvi) {
 if ( lvi && (lvi->rtti() >= DIRECTORY) ) return static_cast<Directory *>(lvi);
 else return NULL;
}

//----------------------------------------------------------------------------

void Directory::activateItem(bool resetCache, int tab /*=7*/)
{
//    qDebug("activateItem("+QString(resetCache ? "true" : "false")+")");
   
   if (static_cast<CvsDirListView*>(s_topView)->validate(s_activeDir)) {
      s_activeDir->saveViewPort();
   }
   bool dirChanged = (this != (DirBase *)s_activeDir);
   s_activeDir = this;

   CFileListView * lv = static_cast<CFileListView*>(s_pListView->page(m_tabIdx));
   assert(lv);

   bool update = (lv->isUpdatable());
   if (update) lv->setUpdatable(false);
   
   if (tab) {
      if (tab & Controled) {
	 m_pFileListView->clear();
	 m_pFileListView->setColumnWidth(0,100);
	 m_pFileListView->setColumnWidth(1,100);
	 m_pFileListView->setColumnWidth(2,50);
	 m_pFileListView->setColumnWidth(3,50);
	 m_pFileListView->setColumnWidth(4,50);
	 m_pFileListView->setColumnWidth(5,100);
	 m_pFileListView->setColumnWidth(6,100);
	 m_pFileListView->setCurAbsPath(m_fullName);
      }
      if (tab & NonControled) {
	 m_pNonFileListView->clear();
	 m_pNonFileListView->setColumnWidth(0,100);
	 m_pNonFileListView->setColumnWidth(1,100);
	 m_pNonFileListView->setCurAbsPath(m_fullName);
      }
      if (tab & Ignored) {
	 m_pIgnoreFileListView->clear();
	 m_pIgnoreFileListView->setColumnWidth(0,100);
	 m_pIgnoreFileListView->setColumnWidth(1,100);
	 m_pIgnoreFileListView->setCurAbsPath(m_fullName);
      }
      
      if (!resetCache) isModified();//reset modified flag to prevent double checking on next checkStatus()
      
      if (!m_disabled) {//don't activate if disabled
	 qApp->setOverrideCursor(waitCursor); // takes some time on huge dirs
	 checkAndShowStatus(NULL,false,tab,resetCache);
	 qApp->restoreOverrideCursor();
      }
      m_pNonFileListView->signalState();
      
      QListViewItem *item = NULL;
      for ( QStringList::Iterator it = m_selectedList.begin(); it != m_selectedList.end(); ++it ) {
	 item = lv->findItem(*it,0);
	 if (item) item->setSelected(true);
      }
      if (item) lv->setCurrentItem(item);
      
   } else {
      m_pNonFileListView->signalState();
   }
   
   s_pListView->setCurrentPage(m_tabIdx);

   if (update) {
      m_pFileContentsChanged->setValue(true);
      m_pFileContentsChanged->activate();
      if (dirChanged) lv->setUpdatable(true);
      lv->horizontalScrollBar()->setValue(m_lastHScrollBarValue);
      lv->verticalScrollBar()->setValue(m_lastVScrollBarValue);
      if (!dirChanged) lv->setUpdatable(true);
   }
}

//----------------------------------------------------------------------------

void Directory::saveViewPort() {
   m_tabIdx = s_pListView->currentPageIndex();
   CFileListView * lv = s_pListView->currentPage();
   m_lastHScrollBarValue = lv->horizontalScrollBar()->value();
   m_lastVScrollBarValue = lv->verticalScrollBar()->value();
   m_selectedList.clear();
   QListViewItem * myChild = lv->firstChild();
   while(myChild) {
      if (myChild->isSelected()) m_selectedList.append(myChild->text(0));
      myChild = myChild->nextSibling();
   }
}

//----------------------------------------------------------------------------

void Directory::init(const QString& fileName)
{
   m_isControlledDir = false;
   m_hasControlledSubDir = false;
   m_isAnalyzedDir = false;
   m_activatingIsNecessary = true;

   m_pFileListView = s_FileListView;
   m_pNonFileListView = s_NonFileListView;
   m_pIgnoreFileListView = s_IgnoreFileListView;
   m_fullName = fileName;
   m_readable = QDir(m_fullName).isReadable();
   m_entries.setAutoDelete(true);
   m_entries = QDict<EntryState>(nextPrime(0));
   m_ignoreFiles = QDict<bool>(nextPrime(0));
   m_ignoreFiles.setAutoDelete(true);
   m_nonFiles = QDict<bool>(nextPrime(0));
   m_nonFiles.setAutoDelete(true);
   m_curState = ES_unknown;
   m_port = -1;
   m_isAutoUpdate = FALSE;
   m_dirTag = QString::null;

   setStatusText(fileName);

   if(m_fullName.length() == 1) {//root
      setText(0, m_fullName);
   } else {
      setText(0, QFileInfo( m_fullName ).fileName() );
   }   

   // init global state variables
   m_curStateMax = ES_probably_up_to_date;
   m_curDirStateMax = ES_probably_up_to_date;

   //check for CVS/ directory
   m_isControlledDir = checkDirForControl();

   //check for being a sub project and adapt disabledList
   if (m_isSubProjectRoot) {
      int au = 0;
      QString subProjectName = relativeName();
      projectSettings->get(subProjectName,DISABLED,m_disabledList);
      projectSettings->get(subProjectName,PAUTOUPDATE,au);
      if (AUTOUPDATE && (au>0) ) {
	 m_isAutoUpdate = true;
	 m_topDir->setAutoUpdate(true);
      }
   }

   // clear directory
   setDirState( ES_probably_up_to_date );

   bool dontAnalyze = false;
   if (m_virtual) {
      m_disabled = false;
      setDirState(ES_missing_dir);
      dontAnalyze = true;
   } else if (!m_readable ) {
      m_disabled = true;
      dontAnalyze = true;
   } else if ( getDisabled(m_fullName) ) {
      m_disabled = true;
      Directory *hlp = parent();
      if (hlp) hlp->setDirState(ES_unknown,true); // reset state in dir-hierarchy
      dontAnalyze = true;
   }
   if (dontAnalyze) {
      setDirIcon();
      return;
   }
   QFileInfo fInfo(m_fullName);
   if (fInfo.exists()) {
      m_lastModTimeOfDir = fInfo.lastModified();
      m_lastCallTime = m_lastModTimeOfDir;
   }
   m_disabled = false;
   m_hasDSTBug = hasDSTBug(&m_fullName);
   qApp->processEvents(1);
   if(!globalStopAction){
      if ( !((depth() >= 1) && (ONTHEFLYSCANNING)) ) {
	 analyzeDirs();
      }
      if (m_isControlledDir) {
	 addMonitoredDir(this);
      }
   }
   setDirIcon();
}

//----------------------------------------------------------------------------

void Directory::setOpen( bool open) {
   QString removed;
   setOpen(open,removed);
}

//----------------------------------------------------------------------------

void Directory::setOpen( bool open, QString &removed) {

   bool update;
   if (update = s_topView->isUpdatesEnabled()) {
      s_topView->setUpdatesEnabled(FALSE);
   }

   removed = "";
   if (ONTHEFLYSCANNING && open) {
      QApplication::setOverrideCursor(waitCursor);
      Directory * C = firstChild();
      while( C ) {
	 if ( (!C->m_isAnalyzedDir) && (!C->m_disabled)) {
	    C->analyzeDirs();
	    if (!C->isControlledTree() && !C->isVirtual() && (C->childCount() == 0) ) {
	       Directory * tmp = C->nextSibling();
	       delete C;
	       C = tmp;
	       continue;
	    }
	 }
	 C = C->nextSibling();
      }

      if (!isControlledTree() && !isVirtual() && (childCount() == 0)) {//can't kill myself -- so call cvscontrol to sync with the event loop
	 Directory * tmp = this;
	 while (tmp->parent() &&
	       !(tmp->parent()->isControlledTree()) &&
	       (tmp->parent()->childCount() == 1) &&
	       (tmp->parent() != m_topDir) ) {

	    tmp = tmp->parent();
	 }
	 m_topDir->m_pDirectorySuicide->setValue(tmp->fullName());
	 m_topDir->m_pDirectorySuicide->activate();
	 removed = tmp->fullName();
      }

      setStatusText("");
      QApplication::restoreOverrideCursor();
   }
   QListViewItem::setOpen(open);
   if (update) {
      s_topView->setUpdatesEnabled(TRUE);
      s_topView->triggerUpdate();
   }
}

//----------------------------------------------------------------------------

DirBase * Directory::topDir() {
   return m_topDir;
}

//----------------------------------------------------------------------------

DirBase * Directory::topControlledDir() {
   if (!m_isSubProjectRoot) {
      Directory * dir = parent();
      Q_CHECK_PTR(dir);
      return dir->topControlledDir();
   } else return this;
}

//----------------------------------------------------------------------------

void Directory::analyzeAll() {
  
   bool tmpScanMode = ONTHEFLYSCANNING;

   ONTHEFLYSCANNING = false;

   bool update;
   if (update = s_topView->isUpdatesEnabled()) {
      s_topView->setUpdatesEnabled(FALSE);
   }

   recAnalyzeAll();

   if (update) {
      s_topView->setUpdatesEnabled(TRUE);
      s_topView->triggerUpdate();
   }

   ONTHEFLYSCANNING = tmpScanMode;
}

//----------------------------------------------------------------------------

//This must only be called if not in onTheFlyScanning mode!
bool Directory::recAnalyzeAll() {

   Directory * C = firstChild();
   while( C ) {
      bool remove = false;
      if ( !C->m_disabled) {
	 if (!C->m_isAnalyzedDir) {
	    C->analyzeDirs();
	    if (!C->isControlledTree()) {
	       remove = true;
	    }
	 } else if (!C->recAnalyzeAll()) {
	    remove = true;
	 }
      }
      if (remove) {
	 Directory * tmp = C;
	 C = C->nextSibling();
	 delete tmp;
	 continue;
      } else C = C->nextSibling();
   }

   if (!isControlledTree()) return false;
   else return true;
}

//----------------------------------------------------------------------------

//used from disableProjectSlot, enableProjectSlot, and rereadProjectOfDir
DirBase * Directory::reloadChild(QString childDir) {

   bool update;
   if (update = s_topView->isUpdatesEnabled()) {
      s_topView->setUpdatesEnabled(FALSE);
   }
   Directory * C = firstChild();

   while( C ) {
      if( C->fullName().find(childDir)==0) {
	 delete C;
	 C = 0;   //paranoia
	 break;
      }
      C = C->nextSibling();
   }

   setDirState( ES_unknown, true);//reset state hierarchy

   C = createChildInstance(this, childDir);

   if (ONTHEFLYSCANNING && (!C->m_disabled)) {//won't scan otherwise
      C->analyzeDirs();
   }
   if (update) {
      s_topView->setUpdatesEnabled(TRUE);
      s_topView->triggerUpdate();
   }
   return C;
}

//----------------------------------------------------------------------------

void Directory::removeChild(QString childDir) {

   Directory * C = firstChild();
   while( C ) {
      if( C->fullName().find(childDir)==0) {
	 delete C;
	 break;
      }
      C = C->nextSibling();
   }
   setDirState( ES_unknown, true);//reset state hierarchy
}

//----------------------------------------------------------------------------

void Directory::removeChild(DirBase * childDir) {
   delete childDir;
   childDir = 0;
   setDirState( ES_unknown, true);//reset state hierarchy
}

//----------------------------------------------------------------------------

QString Directory::shortName( void ) {

   QString R;
   int pos = m_fullName.findRev( '/' );

   R = m_fullName.mid( pos+1 );
   return R;
}

//----------------------------------------------------------------------------

QString Directory::relativeName() {

   QString rel;
   int pos = m_topDir->m_fullName.findRev( '/');
   rel = m_fullName.mid( pos+1);
   return rel;
}

/*---------------------------------------------------------------------------*/
/*!
  \fn			void Directory::paintCell( QPainter *p, const QColorGroup &cg,
  int column, int width, int alignment )
  \brief		Paints a dedicated directory entry with a different color.

  \param		*p		
  \param		&cg		
  \param		column		
  \param		width		
  \param		alignment		

  <BR><HR>*/
/*---------------------------------------------------------------------------*/

void Directory::paintCell( QPainter *p, const QColorGroup &cg,
      int column, int width, int alignment )
{
   QColorGroup colGrp( cg );
   QColor c = colGrp.text();
   bool translate = false;

   if (m_disabled) {
      colGrp.setColor( QColorGroup::Text, Qt::gray );
   }
   if ( m_isSubProjectRoot && (m_topDir != this) ) {
      translate = true;
      if (isSelected()) {
	 p->fillRect(0,0,14,height(),colGrp.highlight());
      } else {
	 p->eraseRect(0,0,14,height());
      }
      p->drawPixmap(2,0,findEmbeddedPixmap("anchor_12x16"));
      p->translate(14,0);
   }
   QListViewItem::paintCell( p, colGrp, column, width, alignment );
   colGrp.setColor( QColorGroup::Text, c );
   if (translate) p->translate(-14,0);
}                                                                           


//----------------------------------------------------------------------------

//called after checkout, if it's a part of an existing project, and after UPDATE_DIR_CMD and QUERY_UPDATE_CMD
//returns the first not yet analyzed directory in the hierarchy
//if addVirtual==true, add as virtual dir, itter, don't analyze!
DirBase * Directory::addDir(QString newDir, bool addVirtual) {

   if (!m_isAnalyzedDir && !m_virtual ) {//don't add if scanning hasn't reached this depth yet!
      return this;
   }

   if (m_fullName.compare(newDir) == 0) {//I am it ;-)

      if (addVirtual) return NULL;
      resetDirState();
      Directory *myChild = firstChild();
      while (myChild) {
	 Directory *myTmpChild = myChild;
	 myChild = myChild->nextSibling();
	 delete myTmpChild;
      }
      m_isAnalyzedDir = false;//need to rescan!
      analyzeDirs();
      return NULL;

   } else {//It's in one of the subdir's path

      Directory *myChild = firstChild();
      while(myChild) {
	 if(newDir.find(myChild->fullName()+"/" ) == 0) {
	    return myChild->addDir(newDir, addVirtual);
	 }
	 myChild = myChild->nextSibling();
      }
   }

   //Not found yet? So it's a new subdir
   Directory * item = NULL;
   if (!addVirtual) {
      item = createChildInstance(this, newDir);
     
      if(item->isControlledTree()) { // fails if dir is disabled
	 m_hasControlledSubDir = true;
      }
      else if (!item->m_disabled) {
	 delete item;
	 item = 0;
      }
      calcDirChilds_ES_Max();
      if (item && (!item->m_isAnalyzedDir)) {
	 return item;
      } else return NULL;

   } else {//virtual dir (query update)

      QString newName = newDir;
      int pos = newName.find( "/", m_fullName.length()+1);
      if (pos > -1) {
	 newName = newDir.left(pos);
      }
      item = createChild(newName, true);

      item->addDir( newDir, true);//wouldn't itter otherwise cause not analyzed
      return item;//item must never be analyzed, it is virtual!

   }

}

//----------------------------------------------------------------------------

DirBase * Directory::searchDirOfPath(QString path, bool findVirtual) {
   if (!findVirtual && m_virtual) {
      return NULL;
   }
   if (path.compare(m_fullName) == 0) {
      return this;   //it's my path :-)
   }
  
   if (path.find(m_fullName+"/") == 0) {//is subdir
      Directory * myChild = firstChild();
      Directory * result;
      while (myChild) {
         if ( (result = dyncast_dir(myChild->searchDirOfPath(path, findVirtual))) ) {
            return result;
         }
         else {
            myChild = myChild->nextSibling();
         }
      }
   }
   return NULL;
}

//----------------------------------------------------------------------------

//return the deepest valid (not disabled, not virtual) dir in hierarchy
DirBase * Directory::searchLastValidDirOfPath(QString path) {
   if (m_disabled || m_virtual) {
      return NULL;
   }
   if (path.compare(m_fullName) == 0) {
      return this;   //it´s my path :-)
   }
   if (path.find(m_fullName+"/") == 0) {//is subdir
      Directory * myChild = firstChild();
      Directory * result;
      while (myChild) {
	 if ( (result = dyncast_dir(myChild->searchLastValidDirOfPath(path))) ) {
	    return result;
	 }
	 else {
	    myChild = myChild->nextSibling();
	 }
      }
      return this;
   }
   return NULL;
}

//--------------------------------------- STATE -------------------------------------

void Directory::recResetDirState( ) {
   if ( m_disabled ) return; // we don't want to scan and change disabled dirs
   Directory * D;
   m_curState = ES_probably_up_to_date;
   setDirIcon();
   D = firstChild();
   while( D ) {
      D->recResetDirState();
      D = D->nextSibling();
   }
}

//----------------------------------------------------------------------------

void Directory::resetDirState( ) {
   // reset from top down
   m_topDir->recResetDirState();
}

//----------------------------------------------------------------------------

void Directory::setDirState( EntryStates newState, bool fromChild) {

   //   printf("\n%s:\nm_curState: %i, newState: %i, m_curStateMax: %i, m_curDirStateMax: %i\n",fullName().ascii(),m_curState,newState,m_curStateMax,m_curDirStateMax);
   if ( newState == m_curState ) {
      if (fromChild) {//if child state has changed it still must be adapted
	 if (newState > m_curDirStateMax) m_curDirStateMax = newState;
	 else if (newState < m_curDirStateMax) calcDirChilds_ES_Max();
      }
      return;
   }

   EntryStates nextState;

   if( newState > m_curState ) {
      // forced or more severe
      nextState = newState;
      if (fromChild) m_curDirStateMax = newState;
   }
   else { // still warnlevel required
      if (fromChild) calcDirChilds_ES_Max();
      EntryStates dirMaxState = m_curDirStateMax;
      if ( m_curStateMax > dirMaxState) dirMaxState = m_curStateMax;
      nextState = dirMaxState; // set new state
   }

   //   printf("m_curState: %i, newState: %i, m_curStateMax: %i, m_curDirStateMax: %i\n",m_curState,newState,m_curStateMax,m_curDirStateMax);

   // only change pix if required
   if (m_curState<=ES_up_to_date && nextState<=ES_up_to_date) {
      m_curState = nextState;
      return; // same level (ok), nothing to be done
   }
   else if (m_curState>=ES_conflict && nextState>=ES_conflict) {
      m_curState = nextState;
      return; // same level (problem) nothing to be done
   }
   else if (m_curState>ES_modified && nextState>ES_modified && m_curState<ES_needs_patch && nextState<ES_needs_patch) {
      m_curState = nextState;
      return; // same level (uncommitted) nothing to be done
   }
   else if (m_curState>=ES_needs_patch && nextState>=ES_needs_patch && m_curState<ES_conflict && nextState<ES_conflict) {
      m_curState = nextState;
      return; // same level (warn) nothing to be done
   }
   else m_curState = nextState;

   // now set the pixmap
   setDirIcon();

   if( depth() > 0 ) {
      // non root -> has directory parent
      parent()->setDirState( nextState, true);
   }
}

//----------------------------------------------------------------------------

EntryStates Directory::getEntryState(QString fileName) {
   EntryState * e = m_entries.find(fileName.stripWhiteSpace());
   return (e) ? e->state() : ES_invalid;
}

//----------------------------------------------------------------------------

void Directory::setFilesToUpToDate(QStringList& updateFileList)
{
   QString tmp;
   while (!updateFileList.isEmpty()) {
      tmp = updateFileList.first();
      setEntryState(tmp, ES_up_to_date);
      updateFileList.remove(tmp);
   }
}

//----------------------------------------------------------------------------

void Directory::setDirIcon() {

   const char * PMName;
   if ( m_disabled) {
      PMName = "FolderDisabled16x16";
   } else if ( m_virtual) {//virtual folder
      PMName = "FolderMissing16x16";
   } else if ( m_curState <= ES_up_to_date ) {// all is OK
      PMName = (m_isControlledDir) ?
	 (m_readable) ?
	 (m_isAutoUpdate) ? "FolderClosedAuto26x16" : "FolderClosed16x16"
	 : "FolderClosedLocked16x16"
	 : (m_isAutoUpdate) ? "FolderStatusAuto26x16" : "FolderStatus16x16";
   } else if ( m_curState == ES_noncvs) { // noncvs
      PMName = (m_isControlledDir) ?
	 (m_readable) ?
	 (m_isAutoUpdate) ? "FolderClosedNonCVSAuto26x16" :  "FolderClosedNonCVS16x16"
	 : "FolderClosedLockedNonCVS16x16"
	 : (m_isAutoUpdate) ? "FolderStatusAuto26x16" : "FolderStatus16x16";
   } else if ( m_curState == ES_modified) { // modified
      PMName = (m_isControlledDir) ?
	 (m_readable) ?
	 (m_isAutoUpdate) ? "FolderClosedModifiedAuto26x16" : "FolderClosedModified16x16"
	 : "FolderClosedLockedModified16x16"
	 : (m_isAutoUpdate) ? "FolderStatusAuto26x16" : "FolderStatus16x16";
   } else if( m_curState < ES_needs_patch ) { // uncommitted
      PMName = (m_isControlledDir) ?
	 (m_readable) ?
	 (m_isAutoUpdate) ? "FolderClosedUncommittedAuto26x16" : "FolderClosedUncommitted16x16"
	 : "FolderClosedLockedUncommitted16x16"
	 : (m_isAutoUpdate) ? "FolderStatusAuto26x16" : "FolderStatus16x16";
   } else if( m_curState < ES_conflict ) { // warn
      PMName = (m_isControlledDir) ?
	 (m_readable) ?
	 (m_isAutoUpdate) ? "FolderClosedWarnAuto26x16" : "FolderClosedWarn16x16"
	 : "FolderClosedLockedWarn16x16"
	 : (m_isAutoUpdate) ? "FolderStatusAuto26x16" : "FolderStatus16x16";
   } else { // problem
      PMName = (m_isControlledDir) ?
	 (m_readable) ?
	 (m_isAutoUpdate) ? "FolderClosedProblemAuto26x16" : "FolderClosedProblem16x16"
	 : "FolderClosedLockedProblem16x16"
	 : (m_isAutoUpdate) ? "FolderStatusAuto26x16" : "FolderStatus16x16";
   }
   setPixmap( 0, findEmbeddedPixmap (PMName));
}

//----------------------------------------------------------------------------

void Directory::setAutoUpdate(bool state) {
   m_isAutoUpdate = state;
   setDirIcon();
}

//----------------------------------------------------------------------------

void Directory::setCallTime(QDateTime & dateTime) {
   m_lastCallTime = dateTime;
   Directory * D = firstChild();
   while( D ) {
      D->setCallTime(dateTime);
      D = D->nextSibling();
   }
}

//----------------------------------------------------------------------------

//----------------------------------------------------------------------------

void Directory::removeTmpEntries(QString name) {
   //remove virtual dirs
   Directory *myChild = firstChild();
   while(myChild) {
      if (myChild->isVirtual()) {
	 Directory *myTmpChild = myChild;
	 myChild = myChild->nextSibling();
	 if (name.isNull() || (name == myTmpChild->shortName()) ) {
	    delete (myTmpChild);
	    setDirState(ES_unknown,true);//reset state
	 }
      } else {
	 myChild = myChild->nextSibling();
      }
   }
}

//----------------------------------------------------------------------------

void Directory::recRemoveTmpEntries(bool allProjects) {
   if (m_disabled) return;
   Directory * C = firstChild();
   while( C ) {
      if (allProjects || (C->topControlledDir() == topControlledDir()) ) C->recRemoveTmpEntries(allProjects);
      C = C->nextSibling();
   }
   removeTmpEntries();
}

//----------------------------------------------------------------------------

void Directory::recGetOpenDirsStringList(QStringList& list) {
   if (!isOpen()) return;
   else list.append(m_fullName);
   Directory * C = firstChild();
   while( C ) {
      C->recGetOpenDirsStringList(list);
      C = C->nextSibling();
   }
}

//----------------------------------------------------------------------------

void Directory::setAllToUpToDate() {
   //alle entries im eignen verzeichnis auf uptodate setzen
   EntriesIterator it(*getEntries());
   while(it.current()) {
      setAndAppendEntry(it.current()->name, ES_up_to_date);
      ++it;
   }
   //jetzt die subdirs...
   Directory *myChild = firstChild();
   while(myChild) {
      myChild->setAllToUpToDate();
      myChild = myChild->nextSibling();
   }
}


//----------------------------------------------------------------------------

void Directory::set_ES_Max(EntryStates e_state) {
   m_curStateMax = e_state;
}

//----------------------------------------------------------------------------

EntryStates Directory::get_ES_Max() {
   return m_curStateMax;
}

//----------------------------------------------------------------------------

void Directory::calc_ES_Max() {
   if (m_nonFiles.isEmpty()) m_curStateMax = ES_unknown;//set modified state if there are non-cvs-files
   else m_curStateMax = ES_noncvs;
   QDictIterator<EntryState>it(m_entries);
   while(it.current()) {
      if (it.current()->state() > m_curStateMax) m_curStateMax = it.current()->state();
      ++it;
   }
}

//----------------------------------------------------------------------------

void Directory::calcDirChilds_ES_Max() {
   m_curDirStateMax = ES_unknown;
   Directory * C = firstChild();
   while( C ) {
      if( C->m_curState > m_curDirStateMax) m_curDirStateMax = C->m_curState;
      C = C->nextSibling();
   }
}

//----------------------------------------------------------------------------

void Directory::setAndAppendEntry(QString &fileName, EntryStates stateId)
{
   if(!setEntryState(fileName, stateId)) {
      appendEntry(fileName, new EntryState(fileName, stateId, this ));
      return;
   }
}

//----------------------------------------------------------------------------

bool Directory::recCopyTree(QString src,QString dst, bool deleteSource /*=TRUE*/) {

   QDir newDir;
   QString dstDir = dst+"/"+src.mid(src.findRev("/"));
   if (!newDir.mkdir(dstDir) ) {
      qDebug("can't create dir: "+dstDir );
      return FALSE;
   }

   //dirs first
   QStringList AllSubDirs;
   QDir D( src);
   D.setFilter(QDir::Dirs);
   AllSubDirs = D.entryList();

   // remove . and ..
   AllSubDirs.remove( "." );
   AllSubDirs.remove( ".." );
  
   QStringList::Iterator fileit;  //VC6 does _not_ like it...
   for (fileit = AllSubDirs.begin(); fileit != AllSubDirs.end(); fileit++) {
      recCopyTree(src+"/"+(*fileit),dstDir,deleteSource);
   }
  
   //now the files
   QStringList AllFilesInDirectory;
   D.setFilter( QDir::Files | QDir::Hidden);
   AllFilesInDirectory = D.entryList();

   for (fileit = AllFilesInDirectory.begin(); fileit != AllFilesInDirectory.end(); fileit++) {
      QString srcName = src+"/"+(*fileit);
      QString dstName = dstDir+"/"+(*fileit);
      QFileInfo fInfo(srcName);
      int permission = 0;
      if (fInfo.isReadable()) permission = (permission | READABLE);
      if (fInfo.isWritable()) permission = (permission | WRITEABLE);
      if (fInfo.isExecutable()) permission = (permission | EXECUTABLE);
      if (!copyFile(srcName, dstName, permission, deleteSource) ) {
	 qDebug("can't copy file: "+srcName+" -> "+dstName);
	 return FALSE;
      }
   }
   return TRUE;
}

//----------------------------------------------------------------------------

bool Directory::recCopyControlledFiles(QString nextDir, int writeMode /*=Create*/) {//rename to recCopyRegisteredFiles

   QDir newDir(nextDir);
   if (writeMode==Create) {
      if (newDir.exists()) {
	 qDebug("dir exists: "+nextDir);
	 return false;
      }
      if (!newDir.mkdir(nextDir)) {
	 qDebug("can't create dir: "+nextDir);
	 return false;
      }
   } else if (writeMode==Replace) {
      if (!newDir.exists()) {
	 if (!newDir.mkdir(nextDir)) {
	    qDebug("can't create dir: "+nextDir);
	    return false;
	 }
      }
   } else if (!newDir.exists()) {
      qDebug("dir doesn't exists: "+nextDir);
      return false;
   }

   QStringList targetFiles = newDir.entryList(QDir::Files|QDir::Hidden|QDir::System);
   Entries * entries = getEntries();
   if (entries) {
      EntriesIterator it(*entries);
      while(it.current()) {
	 QString source = m_fullName+"/"+it.current()->name;
	 QString target = nextDir+"/"+it.current()->name;

	 QFileInfo targetInfo(target);
	 if ( (writeMode & Create) && targetInfo.exists()) {
	    qDebug("Create mode and file exists: "+target);
	    return false;
	 }
	 if ( (writeMode & (Overwrite|Replace)) && targetInfo.exists() && !targetInfo.isWritable()) {
            QFile targetFile(target);
	    setPermission(targetFile,READABLE|WRITEABLE|(targetInfo.isExecutable() ? EXECUTABLE : 0));
	 }

	 QFileInfo info(source);
	 if (info.exists()) {
	    int permission = READABLE;
	    if (info.isWritable()) permission |= WRITEABLE;
	    if (info.isExecutable()) permission |= EXECUTABLE;
	    if (!copyFile(source,target,permission)) {
	       qDebug("can't copy file: "+source+" -> "+target);
	       return false;
	    } else {
	       QStringList::Iterator iter = targetFiles.find(it.current()->name);
	       if (iter != targetFiles.end()) {
		  targetFiles.remove(iter);
	       }
	    }
	 }
	 ++it;
      }
   }
   if (writeMode & Replace) {//remove remaining files
      QStringList::Iterator iter = targetFiles.begin();
      while (iter!=targetFiles.end()) {
         QFile targetFile(*iter);
	 setPermission(targetFile,WRITEABLE);
	 if (!newDir.remove(*iter)) {
	    qDebug("cannot remove file: "+nextDir+"/"+*iter);
	 }
	 ++iter;
      }
   }

   Directory * child = firstChild();
   while( child) {
      if ( !(child->m_disabled || child->m_virtual) ) {
	 if (!child->recCopyControlledFiles(nextDir+"/"+child->shortName(),writeMode)) {
	    return false;
	 }
      }
      child = child->nextSibling();
   }
   return true;
}

//----------------------------------------------------------------------------

bool Directory::copyFile(QString src, QString dst, int permission, bool deleteSource /*=FALSE*/) {

   QFile srcFile( src );
   if (srcFile.open( IO_ReadOnly )) {
      unsigned int size = srcFile.size();
      char * buff = new char[size];
      QDataStream srcStream(&srcFile);
      srcStream.readRawBytes(buff,size);
    
      QFile dstFile( dst);
      if (dstFile.open( IO_WriteOnly )) {
	 QDataStream dstStream(&dstFile);
	 dstStream.writeRawBytes(buff,size);
	 dstFile.close();
      } else {
	 delete buff;
	 return false;
      }
      setPermission(dstFile,permission);
      delete buff;
      srcFile.close();
      if (deleteSource) {
	 setPermission(srcFile,READABLE | WRITEABLE);
	 return srcFile.remove();
      } else {
	 return true;
      }
   } else return false;
}

//----------------------------------------------------------------------------

bool Directory::backupFiles(QStringList) {
   return false;
}

//----------------------------------------------------------------------------

bool Directory::restoreFiles(QStringList) {
   return false;
}

//----------------------------------------------------------------------------

bool Directory::recRemoveNonControlledFiles(QString &filter) {

   QDir D( m_fullName);
   D.setFilter( QDir::Files | QDir::Hidden);
   D.setNameFilter( filter);
   QStringList fileList = D.entryList();

   Entries * entries = getEntries();
   for (QStringList::Iterator fileit = fileList.begin(); fileit != fileList.end(); fileit++) {
      Entry * entry = entries->find(*fileit);
      if (!entry) {
	 QString fileName = m_fullName + "/" + (*fileit);
	 QFile file(fileName);
	 setPermission(file, READABLE | WRITEABLE);
	 if (!file.remove()) {
	    filter = fileName;
	    return false;
	 }
      }
   }

   Directory * child = firstChild();
   while( child) {
      if ( !(child->m_disabled || child->m_virtual) ) {
	 if (!child->recRemoveNonControlledFiles(filter)) {
	    return false;
	 }
      }
      child = child->nextSibling();
   }

   return true;
}

//----------------------------------------------------------------------------

//Check and modify the dir state according to non-cvs files,
//use !!!only!!! for this!!!
void Directory::checkNonControlledFilesDirState() {//rename to checkUncontrolledFilesDirState
   if (m_nonFiles.isEmpty()) {
      if (m_curStateMax == ES_noncvs) {
	 calc_ES_Max();
	 setDirState( m_curStateMax);
      }
   } else if (m_curStateMax < ES_noncvs) {
      m_curStateMax = ES_noncvs;
      setDirState( ES_noncvs);
   }
}

//----------------------------------------------------------------------------

//recursive check for modifications, only detected by a timestamp
//change of the dir or its CVS/Entries file
//if checkCvsFiles is true, also check for a change in each files timestamp
//if modifications are detected, check and set the new dir state
void Directory::recCheckForModifications(bool checkCvsFiles) {

   if (m_disabled || m_virtual) return;

   qApp->processEvents(1);
   if (globalStopAction) return;

   Directory * C = firstChild();
   while( C ) {
      C->recCheckForModifications(checkCvsFiles);
      C = C->nextSibling();
   }
}

//----------------------------------------------------------------------------

//recursive check for modifications, only detected by a timestamp
//change of the CVS/Entries / CVS/Entries.log file
//if modifications are detected, activateItem
bool Directory::postCallCheck(int) {
   return false;
}

//----------------------------------------------------------------------------

void Directory::validateControlledFilesStatus( bool recursive /* = FALSE*/,
      bool forceCacheReset /* = FALSE*/,
      bool forceEntriesReset /* = FALSE*/) {

   if (recursive) {
      Directory * C = firstChild();
      while( C ) {
	 C->validateControlledFilesStatus(recursive,forceCacheReset,forceEntriesReset);
	 C = C->nextSibling();
      }
   }
}

//----------------------------------------------------------------------------

//Add a single file, only known by its name, and display it
void Directory::checkAndUpdateFileCache(QString& file) {

   bool found;
   updateFileCacheAndCheckForIgnores(QString::null,file,false,found);
   if (isSelected() && (!found) ) {
      
      bool IsDir = false;
      bool isWritable = false;
      bool isExecutable = false;
      
      QFileInfo info(m_fullName+"/"+file);
      IsDir = info.isDir();
      isWritable = info.isWritable();
      isExecutable = info.isExecutable();
      
      //show non-controlled entries
      FileListViewItem * item = new FileListViewItem(m_pNonFileListView,IsDir,FileListViewItem::TextNoCase,FileListViewItem::ModDate);
      item->setPixmap(0, findEmbeddedPixmap((IsDir) ?
		  (isExecutable) ?  "FolderStatus16x16" : "FolderClosedLocked16x16" :
		  (isWritable) ? "FileUnknown16x16" : "FileLocked16x16"));
      m_pNonFileListView->signalState();

      item->setText(0, file);
      QDateTime localDate = info.lastModified();
      item->setModifiedDate(1, &localDate, localDate.toString(LookAndFeel::g_dateTimeFormat));
   }
}

//----------------------------------------------------------------------------

//Remove a single file, only known by its name, and remove from display if selected
bool Directory::removeNonControlled(QString& file) {
   if (m_ignoreFiles.remove(file)) {
      if (isSelected()) {
	 QListViewItem * item = m_pIgnoreFileListView->firstChild();
	 while (item) {
	    if (item->text(0) == file) {
	       QListViewItem * hlp = item;
	       item = item->nextSibling();
	       delete hlp;
	       continue;
	    } else item = item->nextSibling();
	 }
      }
      return true;
   } else if (m_nonFiles.remove(file)) {
      if (isSelected()) {
	 QListViewItem * item = m_pNonFileListView->firstChild();
	 while (item) {
	    if (item->text(0) == file) {
	       QListViewItem * hlp = item;
	       item = item->nextSibling();
	       delete hlp;
	       continue;
	    } else item = item->nextSibling();
	 }
      }
      checkNonControlledFilesDirState();
      if (isSelected()) {
	 m_pNonFileListView->signalState();
      }
      return true;
   }
   return false;
}

//----------------------------------------------------------------------------

//Main function for checking file state and displaying, used by DirWatch,AnalyzeDir,cvscontrol::checkStatus ...
void Directory::checkAndShowStatus(FileListViewItem *item /* = NULL*/,
      bool checkOnly /* =FALSE*/,
      int tab /* =Controled|NonControled|Ignored*/,
      bool /*resetCache =FALSE*/) {

   if(item) {//only one item
      qDebug("Directory cannot hold controlled files");
   } else { // read entries in directory
      if (tab & NonControled) {
	 QStringList AllFilesInDirectory;
	 
	 QFile f;
	 QString line;
	 QString path = m_fullName;
	 if(path.length() > 1){//is not root
	    path += "/";
	 }

	 QDir D( path);
	 D.setFilter( QDir::Files | QDir::Dirs | QDir::Hidden | QDir::System);
	 AllFilesInDirectory = D.entryList();

	 // remove . and ..
	 AllFilesInDirectory.remove( "." );
	 AllFilesInDirectory.remove( ".." );

	 // now check the files
	 for (QStringList::Iterator fileit = AllFilesInDirectory.begin(); fileit != AllFilesInDirectory.end(); fileit++) {
      
	    QString name = path + (*fileit);
	    QFileInfo info(name);
	    
	    bool IsDir = info.isDir();
	    if ( IsDir && (checkType(name+"/") != Plain) ) {// Controled directory -> do NOT show
	       continue;
	    }
	    bool isWritable = info.isWritable();
	    bool isExecutable = info.isExecutable();
	    
	    bool found;
	    updateFileCacheAndCheckForIgnores(QString::null,*fileit,false, found);
	    
	    // stop here if checkOnly because now we know the non-cvs state (dir state was allready set in updateFileCacheAndCheckForIgnores
	    if (checkOnly) return;
	    //show non-cvs entries
	    item = new FileListViewItem(m_pNonFileListView,IsDir,FileListViewItem::TextNoCase,FileListViewItem::ModDate);
	    item->setPixmap(0, findEmbeddedPixmap( (IsDir) ?
				  (isExecutable) ?  "FolderStatus16x16" : "FolderClosedLocked16x16" :
				  (isWritable) ? "FileUnknown16x16" : "FileLocked16x16"));
	    
	    item->setText(0, *fileit);
	    QDateTime localDate = info.lastModified();
	    item->setModifiedDate(1, &localDate, localDate.toString(LookAndFeel::g_dateTimeFormat));
	 }
	 checkNonControlledFilesDirState();
      }
   }
}

//----------------------------------------------------------------------------

bool Directory::updateFileCacheAndCheckForIgnores(const QString filesToIgnore, QString& file, bool ignoreCacheReseted, bool& found) {

   bool doIgnore = FALSE;
   found = FALSE;
   if (!ignoreCacheReseted) {
      if (m_ignoreFiles.find(file)) {
         doIgnore = true;
         found = true;
      } else if (m_nonFiles.find(file)) {
         found = true;
      }
   }
   if (!found) {
      //reimplemented old version of ignore scan,
      //now using wildcard mode
      //this is at about factor 2 faster on lin!!!
      QStringList ignf = QStringList::split(" ",filesToIgnore);
      QRegExp exp;
      exp.setWildcard(true);
      
      for (QStringList::Iterator it = ignf.begin(); it != ignf.end(); it++) {
         exp.setPattern(*it);
         if ( exp.exactMatch(file) ) {
            doIgnore = true;
            break;
         }
      }
      if (doIgnore) {
         if (m_ignoreFiles.size()<=m_ignoreFiles.count()) {
            m_ignoreFiles.resize(nextPrime(m_ignoreFiles.size()));
         }
         m_ignoreFiles.insert(file,new bool(true));
      } else {
         if (m_nonFiles.size()<=m_nonFiles.count()) {
            m_nonFiles.resize(nextPrime(m_nonFiles.size()));
         }
         m_nonFiles.insert(file,new bool(true));
         checkNonControlledFilesDirState();
      }
   }
   return doIgnore;
}

//----------------------------------------------------------------------------

void Directory::updateDisabledList(QStringList& list) {
   m_disabledList = list;
}

//----------------------------------------------------------------------------
                      
void Directory::updateSubProjectList(QStringList& list) {
   m_subProjectList = list;
}

//----------------------------------------------------------------------------
                      
bool Directory::getDisabled(QString filename)
{
   QStringList* list = dyncast_dir(topControlledDir())->getDisabledList();
   QStringList::iterator it = list->find( filename);
   if (it != list->end()) {
      return true;
   } else {
      return false;
   }
}

//----------------------------------------------------------------------------
                      
void Directory::setAllOpen( ) {

   Directory * D = this;
   do {
      D->setOpen( TRUE );
      if( D->depth() == 0 ) 
	 // root item 
	 break;
      D = D->parent();
   } while ( 1 );
}

//----------------------------------------------------------------------------
                      
void Directory::expandAllDir() {

   bool update;
   if (update = s_topView->isUpdatesEnabled()) {
      s_topView->setUpdatesEnabled(FALSE);
   }

   if (!isOpen()) {
      setOpen(true);
   }
   Directory *myChild = firstChild();
   while(myChild) {
      myChild->expandAllDir();
      myChild = myChild->nextSibling();
   }

   if (update) {
      s_topView->setUpdatesEnabled(TRUE);
      s_topView->triggerUpdate();
   }
}

//----------------------------------------------------------------------------
                      
void Directory::collapsAllDir() {
   Directory *myChild = firstChild();
   while(myChild) {
      myChild->collapsAllDir();
      myChild = myChild->nextSibling();
   }
   if (isOpen()) {
      setOpen(false);
   }
}

//----------------------------------------------------------------------------
                      
int Directory::isTreeEmpty() {

   if (getEntries()->count()) return 0;

   // seems to contain NO regular CVS files
  
   Directory * C = firstChild();
   while( C ) {
      if( ! C->isTreeEmpty() ) 
	 return 0;
      C = C->nextSibling();
   }
   // no entry found
   return 1;
}

//----------------------------------------------------------------------------
                      
void Directory::afterCall( int, CvsBuffer*, bool) {

}

//----------------------------------------------------------------------------
                      
bool Directory::hasDisabled() {
   if (m_disabled) return TRUE;
   Directory * C = firstChild();
   while( C ) {
      if (C->hasDisabled()) {
	 return TRUE;
      }
      C = C->nextSibling();
   }
   return FALSE;
}

/**
 * Executes a self contained command on the directory structure beginning
 * with the current item.  The target item (and NOT the command) is
 * responsible for making sure that recursive commands are applied to all
 * child items.
 *
 * @param	CvsCommand
 *	command to execute of the directory structure 
 * @return
 *	true on success.  Otherwise false.
 */

bool Directory::execute(CCvsCommand &cmd)
{
   if (m_disabled) return FALSE;
   if (!cmd.isRecursive()) {
      return cmd.execute(*this);
   } else {
      if (ONTHEFLYSCANNING) analyzeAll();
      return recExecute(cmd);
   }
}


/**
 * Undo the changes made by previously executing the given command. 
 *
 * @param	CvsCommand
 *	command to undo
 * @return
 *	true on success.  Otherwise false.
 */

bool Directory::unexecute(CCvsCommand &cmd)
{
   if (m_disabled) return FALSE;
   if (!cmd.isRecursive()) {
      return cmd.unexecute(*this);
   } else {
      if (ONTHEFLYSCANNING) analyzeAll();
      return recUnexecute(cmd);
   }
}

//----------------------------------------------------------------------------

bool Directory::recExecute(CCvsCommand &cmd) {
   bool retVal = TRUE;
   Directory * C = firstChild();
   while( C ) {
      if (!C->m_disabled && !C->m_isSubProjectRoot) {
         if (!C->recExecute(cmd)) retVal = FALSE;
      } else {
         retVal = FALSE;
      }
      C = C->nextSibling();
   }
   if (!cmd.execute(*this)) retVal = FALSE;
   return retVal;
}

//----------------------------------------------------------------------------

bool Directory::recUnexecute(CCvsCommand &cmd) {
   bool retVal = TRUE;
   Directory * C = firstChild();
   while( C ) {
      if (!C->m_disabled && !C->m_isSubProjectRoot) {
         if (!C->recUnexecute(cmd)) retVal = FALSE;
      } else {
         retVal = FALSE;
      }
      C = C->nextSibling();
   }
   if (!cmd.unexecute(*this)) retVal = FALSE;
   return retVal;
}

//----------------------------------------------------------------------------
                      
void Directory::setStatusText( const QString & txt, int ms) {
   ((CvsDirListView*)s_topView)->setStatusBarText( txt, ms);
}

//----------------------------------------------------------------------------

QStringList Directory::getSelectedFiles() {

   QStringList names;
   FileListViewItem * item = s_pListView->currentFirstChild();
   while (item) {
      if ( item->isSelected() ) {
	 names.append( item->text(0).stripWhiteSpace());
      }
      item = item->nextSibling();
   }
   return names;
}

//----------------------------------------------------------------------------
                      
QString Directory::getFirstSelectedFile() {

   FileListViewItem * item = s_pListView->currentFirstChild();
   while (item) {
      if ( item->isSelected() ) {
	 return item->text(0).stripWhiteSpace();
      }
      item = item->nextSibling();
   }
   return QString::null;
}

//======================================= virtual methods =========================================================


bool Directory::analyzeDirs() {
   if (m_isAnalyzedDir) {//don't analyze twice on setOpen()
      return m_hasControlledSubDir;
   }

   //    qDebug("analyzing: "+m_fullName+", depth: "+QString::number(depth()));
   QDir myDir(m_fullName);
   myDir.setFilter( QDir::Dirs | QDir::Hidden);
   QStringList subDirList = myDir.entryList();
   QString path = m_fullName;
   QString curSubDirName;
   QFileInfo fileInfo;
   Directory *item;

   //is the current dir or the parent dir -- ignore it
   subDirList.remove(".");
   subDirList.remove("..");

   if(!subDirList.isEmpty()) {//has subdirs
      if(m_fullName.length() > 1){//is not root
         path += "/";
      }
   
      for(unsigned int i = 0; i < subDirList.count(); i++) {
         if(globalStopAction) break;   //cancel this action

         curSubDirName = subDirList[i];
         fileInfo.setFile(path + curSubDirName);

	 //ignore symlinks
         if( fileInfo.isSymLink()) continue;

	 //ignore non-cvs dirs
	 if (!bSCANNONCVS && (depth() >= 1)) continue;

         if(fileInfo.isReadable() && fileInfo.isExecutable()) {//is a dir and readable

	    //at the moment lacks a test for enough mem   
	    m_isAnalyzedDir = true;//no need to analyze again in onthefly mode

	    item = Directory::createChildInstance(this, path + curSubDirName);

	    if(item->isControlledTree()) { // fails if dir is disabled and returns true even if
	       // there are dirs in between that have no CVS dir
	       m_hasControlledSubDir = true;
	    }
	    else if (item->isDisabled()) continue;
	    else if (ONTHEFLYSCANNING && !item->isAnalyzed()) continue;//don't know yet if there are cvs dirs somewhere in depth
	    else {
	       delete item;
	       item = 0; // paranoia
	    }
	 }
      }

      calcDirChilds_ES_Max();

   } else {//has no subdirs
      m_isAnalyzedDir = true;//no need to analyze again in onthefly mode
   }

   if (m_hasControlledSubDir) {
      // check and set FolderState and therewith initialize caches
      checkAndShowStatus(NULL,true);
      return true;
   } else {
      return false;
   }
}



syntax highlighted by Code2HTML, v. 0.9.1