summaryrefslogtreecommitdiffabout
path: root/microkde/kio
authorMichael Krelin <hacker@klever.net>2007-07-04 11:23:42 (UTC)
committer Michael Krelin <hacker@klever.net>2007-07-04 11:23:42 (UTC)
commita08aff328d4393031d5ba7d622c2b05705a89d73 (patch) (side-by-side diff)
tree8ee90d686081c52e7c69b5ce946e9b1a7d690001 /microkde/kio
parent11edc920afe4f274c0964436633aa632c8288a40 (diff)
downloadkdepimpi-p1.zip
kdepimpi-p1.tar.gz
kdepimpi-p1.tar.bz2
initial public commit of qt4 portp1
Diffstat (limited to 'microkde/kio') (more/less context) (show whitespace changes)
-rw-r--r--microkde/kio/kfile/kurlrequester.cpp10
-rw-r--r--microkde/kio/kfile/kurlrequester.h4
-rw-r--r--microkde/kio/kio/kdirwatch.cpp12
-rw-r--r--microkde/kio/kio/kdirwatch_p.h10
4 files changed, 20 insertions, 16 deletions
diff --git a/microkde/kio/kfile/kurlrequester.cpp b/microkde/kio/kfile/kurlrequester.cpp
index ca94570..ce62da7 100644
--- a/microkde/kio/kfile/kurlrequester.cpp
+++ b/microkde/kio/kfile/kurlrequester.cpp
@@ -1,409 +1,411 @@
/* This file is part of the KDE libraries
Copyright (C) 1999,2000,2001 Carsten Pfeiffer <pfeiffer@kde.org>
library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#include <sys/stat.h>
#ifdef _WIN32_
#else
#include <unistd.h>
#endif
#include <qstring.h>
//US #include <qtooltip.h>
#include <qpushbutton.h>
+//Added by qt3to4:
+#include <QPixmap>
//US #include <kaccel.h>
//US #include <kcombobox.h>
#include <kdebug.h>
#include <kdialog.h>
#include <kfiledialog.h>
#include <kglobal.h>
#include <kiconloader.h>
#include <klineedit.h>
#include <klocale.h>
//US #include <kurlcompletion.h>
//US #include <kurldrag.h>
//US #include <kprotocolinfo.h>
#include "kurlrequester.h"
class KURLDragPushButton : public QPushButton
{
public:
KURLDragPushButton( QWidget *parent, const char *name=0 )
: QPushButton( parent, name ) {
//US setDragEnabled( true );
}
~KURLDragPushButton() {}
void setURL( const KURL& url ) {
m_urls.clear();
m_urls.append( url );
}
/* not needed so far
void setURLs( const KURL::List& urls ) {
m_urls = urls;
}
const KURL::List& urls() const { return m_urls; }
*/
protected:
/*US
virtual QDragObject *dragObject() {
if ( m_urls.isEmpty() )
return 0L;
QDragObject *drag = KURLDrag::newDrag( m_urls, this, "url drag" );
return drag;
}
*/
private:
KURL::List m_urls;
};
/*
*************************************************************************
*/
class KURLRequester::KURLRequesterPrivate
{
public:
KURLRequesterPrivate() {
edit = 0L;
//US combo = 0L;
//US fileDialogMode = KFile::File | KFile::ExistingOnly | KFile::LocalOnly;
}
void setText( const QString& text ) {
/*US
if ( combo )
{
if (combo->editable())
{
combo->setEditText( text );
}
else
{
combo->insertItem( text );
combo->setCurrentItem( combo->count()-1 );
}
}
else
*/
{
edit->setText( text );
}
}
void connectSignals( QObject *receiver ) {
QObject *sender;
/*US if ( combo )
sender = combo;
else
*/
sender = edit;
connect( sender, SIGNAL( textChanged( const QString& )),
receiver, SIGNAL( textChanged( const QString& )));
connect( sender, SIGNAL( returnPressed() ),
receiver, SIGNAL( returnPressed() ));
//US connect( sender, SIGNAL( returnPressed( const QString& ) ),
//US receiver, SIGNAL( returnPressed( const QString& ) ));
}
/*US
void setCompletionObject( KCompletion *comp ) {
if ( combo )
combo->setCompletionObject( comp );
else
edit->setCompletionObject( comp );
}
*/
/**
* replaces ~user or $FOO, if necessary
*/
QString url() {
QString txt = /*US combo ? combo->currentText() : */ edit->text();
/*US KURLCompletion *comp;
if ( combo )
comp = dynamic_cast<KURLCompletion*>(combo->completionObject());
else
comp = dynamic_cast<KURLCompletion*>(edit->completionObject());
if ( comp )
return comp->replacedPath( txt );
else
*/
return txt;
}
KLineEdit *edit;
//US KComboBox *combo;
int fileDialogMode;
QString fileDialogFilter;
};
/*US
KURLRequester::KURLRequester( QWidget *editWidget, QWidget *parent,
const char *name )
: QHBox( parent, name )
{
d = new KURLRequesterPrivate;
// must have this as parent
editWidget->reparent( this, 0, QPoint(0,0) );
//US d->edit = dynamic_cast<KLineEdit*>( editWidget );
d->edit = (KLineEdit*)( editWidget );
//US d->combo = dynamic_cast<KComboBox*>( editWidget );
init();
}
*/
KURLRequester::KURLRequester( QWidget *parent, const char *name )
- : QHBox( parent, name )
+ : Q3HBox( parent, name )
{
d = new KURLRequesterPrivate;
init();
}
KURLRequester::KURLRequester( const QString& url, QWidget *parent,
const char *name )
- : QHBox( parent, name )
+ : Q3HBox( parent, name )
{
d = new KURLRequesterPrivate;
init();
setURL( url );
}
KURLRequester::~KURLRequester()
{
//US delete myCompletion;
delete myFileDialog;
delete d;
}
void KURLRequester::init()
{
myFileDialog = 0L;
myShowLocalProt = false;
mPathIsDir = false;
if (/*US !d->combo && */ !d->edit )
d->edit = new KLineEdit( this, "KURLRequester::KLineEdit" );
myButton = new KURLDragPushButton( this, "kfile button");
- QIconSet iconSet = SmallIconSet("fileopen");
- QPixmap pixMap = iconSet.pixmap( QIconSet::Small, QIconSet::Normal );
+ QIcon iconSet = SmallIconSet("fileopen");
+ QPixmap pixMap = iconSet.pixmap( QIcon::Small, QIcon::Normal );
myButton->setIconSet( iconSet );
myButton->setFixedSize( pixMap.width()+8, pixMap.height()+8 );
//US QToolTip::add(myButton, i18n("Open file dialog"));
connect( myButton, SIGNAL( pressed() ), SLOT( slotUpdateURL() ));
setSpacing( KDialog::spacingHint() );
QWidget *widget = /*US d->combo ? (QWidget*) d->combo : */ (QWidget*) d->edit;
setFocusProxy( widget );
d->connectSignals( this );
connect( myButton, SIGNAL( clicked() ), this, SLOT( slotOpenDialog() ));
/*US
myCompletion = new KURLCompletion();
d->setCompletionObject( myCompletion );
KAccel *accel = new KAccel( this );
accel->insert( KStdAccel::Open, this, SLOT( slotOpenDialog() ));
accel->readSettings();
*/
}
void KURLRequester::setURL( const QString& url )
{
bool hasLocalPrefix = (url.startsWith("file:"));
if ( !myShowLocalProt && hasLocalPrefix )
d->setText( url.mid( 5, url.length()-5 ));
else
d->setText( url );
}
void KURLRequester::setCaption( const QString& caption )
{
//US fileDialog()->setCaption( caption );
//US QWidget::setCaption( caption );
}
QString KURLRequester::url() const
{
return d->url();
}
void KURLRequester::slotOpenDialog()
{
emit openFileDialog( this );
//US use our special KFIleDialog instead
KURL u( url() );
//QString fn = u.url();
QString fn = d->edit->text();
if ( mPathIsDir )
fn = KFileDialog::getExistingDirectory ( fn, "", this );
else
fn = KFileDialog::getSaveFileName( fn, "", this );
if ( fn == "" )
return;
setURL( fn );
emit urlSelected( d->url() );
/*US
KFileDialog *dlg = fileDialog();
if ( !d->url().isEmpty() ) {
KURL u( url() );
// If we won't be able to list it (e.g. http), then don't try :)
if ( KProtocolInfo::supportsListing( u.protocol() ) )
dlg->setSelection( u.url() );
}
if ( dlg->exec() == QDialog::Accepted )
{
setURL( dlg->selectedURL().prettyURL() );
emit urlSelected( d->url() );
}
*/
}
void KURLRequester::setMode(unsigned int mode)
{
/*US
Q_ASSERT( (mode & KFile::Files) == 0 );
d->fileDialogMode = mode;
if ( (mode & KFile::Directory) && !(mode & KFile::File) )
myCompletion->setMode( KURLCompletion::DirCompletion );
if (myFileDialog)
myFileDialog->setMode( d->fileDialogMode );
*/
}
void KURLRequester::setFilter(const QString &filter)
{
/*US
d->fileDialogFilter = filter;
if (myFileDialog)
myFileDialog->setFilter( d->fileDialogFilter );
*/
}
KFileDialog * KURLRequester::fileDialog() const
{
/*US
if ( !myFileDialog ) {
QWidget *p = parentWidget();
myFileDialog = new KFileDialog( QString::null, QString::null, p,
"file dialog", true );
myFileDialog->setMode( d->fileDialogMode );
myFileDialog->setFilter( d->fileDialogFilter );
}
return myFileDialog;
*/
return 0;
}
void KURLRequester::setShowLocalProtocol( bool b )
{
if ( myShowLocalProt == b )
return;
myShowLocalProt = b;
setURL( url() );
}
void KURLRequester::clear()
{
d->setText( QString::null );
}
KLineEdit * KURLRequester::lineEdit() const
{
return d->edit;
}
/*US
KComboBox * KURLRequester::comboBox() const
{
return d->combo;
}
*/
void KURLRequester::slotUpdateURL()
{
// bin compat, myButton is declared as QPushButton
//US KURL u( QDir::currentDirPath() + '/', url() );
KURL u( url() );
(static_cast<KURLDragPushButton *>( myButton))->setURL( u );
}
QPushButton * KURLRequester::button() const
{
return myButton;
}
/*US
KEditListBox::CustomEditor KURLRequester::customEditor()
{
setSizePolicy(QSizePolicy( QSizePolicy::Preferred,
QSizePolicy::Fixed));
KLineEdit *edit = d->edit;
if ( !edit && d->combo )
edit = dynamic_cast<KLineEdit*>( d->combo->lineEdit() );
#ifndef NDEBUG
if ( !edit )
kdWarning() << "KURLRequester's lineedit is not a KLineEdit!??\n";
#endif
KEditListBox::CustomEditor editor( this, edit );
return editor;
}
*/
void KURLRequester::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }
/*US
KURLComboRequester::KURLComboRequester( QWidget *parent,
const char *name )
: KURLRequester( new KComboBox(false), parent, name)
{
}
*/
//US #include "kurlrequester.moc"
diff --git a/microkde/kio/kfile/kurlrequester.h b/microkde/kio/kfile/kurlrequester.h
index faa3326..5d4fa11 100644
--- a/microkde/kio/kfile/kurlrequester.h
+++ b/microkde/kio/kfile/kurlrequester.h
@@ -1,271 +1,271 @@
/* This file is part of the KDE libraries
Copyright (C) 1999,2000,2001 Carsten Pfeiffer <pfeiffer@kde.org>
library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#ifndef KURLREQUESTER_H
#define KURLREQUESTER_H
-#include <qhbox.h>
+#include <q3hbox.h>
#include <keditlistbox.h>
//US #include <kfile.h>
//US #include <kpushbutton.h>
#include <kurl.h>
//US class KComboBox;
class KFileDialog;
class KLineEdit;
//US class KURLCompletion;
class KURLDragPushButton;
class QPushButton;
class QString;
class QTimer;
/**
* This class is a widget showing a lineedit and a button, which invokes a
* filedialog. File name completion is available in the lineedit.
*
* The defaults for the filedialog are to ask for one existing local file, i.e.
* KFileDialog::setMode( KFile::File | KFile::ExistingOnly | KFile::LocalOnly )
* The default filter is "*", i.e. show all files, and the start directory is
* the current working directory, or the last directory where a file has been
* selected.
*
* You can change this behavior by using @ref setMode() or @ref setFilter().
*
* @short A widget to request a filename/url from the user
* @author Carsten Pfeiffer <pfeiffer@kde.org>
*/
-class KURLRequester : public QHBox
+class KURLRequester : public Q3HBox
{
Q_OBJECT
Q_PROPERTY( QString url READ url WRITE setURL )
public:
/**
* Constructs a KURLRequester widget.
*/
KURLRequester( QWidget *parent=0, const char *name=0 );
/**
* Constructs a KURLRequester widget with the initial URL @p url.
*/
KURLRequester( const QString& url, QWidget *parent=0, const char *name=0 );
/**
* Special constructor, which creates a KURLRequester widget with a custom
* edit-widget. The edit-widget can be either a KComboBox or a KLineEdit
* (or inherited thereof). Note: for geometry management reasons, the
* edit-widget is reparented to have the KURLRequester as parent.
* @param modal specifies whether the filedialog should be opened as modal
* or not.
*/
//US KURLRequester( QWidget *editWidget, QWidget *parent, const char *name=0 );
/**
* Destructs the KURLRequester.
*/
~KURLRequester();
/**
* @returns the current url in the lineedit. May be malformed, if the user
* entered something weird. ~user or environment variables are substituted
* for local files.
*/
QString url() const;
/**
* Enables/disables showing file:/ in the lineedit, when a local file has
* been selected in the filedialog or was set via @ref setURL().
* Default is false, not showing file:/
* @see #showLocalProtocol
*/
void setShowLocalProtocol( bool b );
/**
* Sets the mode of the file dialog.
* Note: you can only select one file with the filedialog,
* so KFile::Files doesn't make much sense.
* @see KFileDialog::setMode()
*/
void setMode( unsigned int m );
void setPathIsDir( ) {mPathIsDir = true;}
/**
* Sets the filter for the file dialog.
* @see KFileDialog::setFilter()
*/
void setFilter( const QString& filter );
/**
* @returns whether local files will be prefixed with file:/ in the
* lineedit
* @see #setShowLocalProtocol
*/
bool showLocalProtocol() const { return myShowLocalProt; }
/**
* @returns a pointer to the filedialog
* You can use this to customize the dialog, e.g. to specify a filter.
* Never returns 0L.
*/
virtual KFileDialog * fileDialog() const;
/**
* @returns a pointer to the lineedit, either the default one, or the
* special one, if you used the special constructor.
*
* It is provided so that you can e.g. set an own completion object
* (e.g. @ref KShellCompletion) into it.
*/
KLineEdit * lineEdit() const;
/**
* @returns a pointer to the combobox, in case you have set one using the
* special constructor. Returns 0L otherwise.
*/
//US KComboBox * comboBox() const;
/**
* @returns a pointer to the pushbutton. It is provided so that you can
* specify an own pixmap or a text, if you really need to.
*/
QPushButton * button() const;
/**
* @returns the KURLCompletion object used in the lineedit/combobox.
*/
//US KURLCompletion *completionObject() const { return myCompletion; }
/**
* @returns an object, suitable for use with KEditListBox. It allows you
* to put this KURLRequester into a KEditListBox.
* Basically, do it like this:
* <pre>
* KURLRequester *req = new KURLRequester( someWidget );
* [...]
* KEditListBox *editListBox = new KEditListBox( i18n("Some Title"), req->customEditor(), someWidget );
* </pre>
* @since 3.1
*/
//US KEditListBox::CustomEditor customEditor();
public slots:
/**
* Sets the url in the lineedit to @p url. Depending on the state of
* @ref showLocalProtocol(), file:/ on local files will be shown or not.
* @since 3.1
*/
void setURL( const QString& url );
/**
* @reimp
* Sets the caption of the file dialog.
* @since 3.1
*/
virtual void setCaption( const QString& caption );
/**
* Clears the lineedit/combobox.
*/
void clear();
signals:
// forwards from LineEdit
/**
* Emitted when the text in the lineedit changes.
* The parameter contains the contents of the lineedit.
* @since 3.1
*/
void textChanged( const QString& );
/**
* Emitted when return or enter was pressed in the lineedit.
*/
void returnPressed();
/**
* Emitted when return or enter was pressed in the lineedit.
* The parameter contains the contents of the lineedit.
*/
void returnPressed( const QString& );
/**
* Emitted before the filedialog is going to open. Connect
* to this signal to "configure" the filedialog, e.g. set the
* filefilter, the mode, a preview-widget, etc. It's usually
* not necessary to set a URL for the filedialog, as it will
* get set properly from the editfield contents.
*
* If you use multiple KURLRequesters, you can connect all of them
* to the same slot and use the given KURLRequester pointer to know
* which one is going to open.
*/
void openFileDialog( KURLRequester * );
/**
* Emitted when the user changed the URL via the file dialog.
* The parameter contains the contents of the lineedit.
*/
void urlSelected( const QString& );
protected:
void init();
//US KURLCompletion * myCompletion;
private:
KURLDragPushButton * myButton;
bool myShowLocalProt;
mutable KFileDialog * myFileDialog;
bool mPathIsDir;
protected slots:
/**
* Called when the button is pressed to open the filedialog.
* Also called when @ref KStdAccel::Open (default is Ctrl-O) is pressed.
*/
void slotOpenDialog();
private slots:
void slotUpdateURL();
protected:
virtual void virtual_hook( int id, void* data );
private:
class KURLRequesterPrivate;
KURLRequesterPrivate *d;
};
/*US
class KURLComboRequester : public KURLRequester // For use in Qt Designer
{
Q_OBJECT
public:
*/
/**
* Constructs a KURLRequester widget with a combobox.
*/
/*US
KURLComboRequester( QWidget *parent=0, const char *name=0 );
};
*/
#endif // KURLREQUESTER_H
diff --git a/microkde/kio/kio/kdirwatch.cpp b/microkde/kio/kio/kdirwatch.cpp
index 1596d1f..5f07c54 100644
--- a/microkde/kio/kio/kdirwatch.cpp
+++ b/microkde/kio/kio/kdirwatch.cpp
@@ -1,1394 +1,1396 @@
// -*- c-basic-offset: 2 -*-
/* This file is part of the KDE libraries
Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
/*
Enhanced Version of the file for platform independent KDE tools.
Copyright (c) 2004 Ulf Schenk
$Id$
*/
// CHANGES:
// Februar 2002 - Add file watching and remote mount check for STAT
// Mar 30, 2001 - Native support for Linux dir change notification.
// Jan 28, 2000 - Usage of FAM service on IRIX (Josef.Weidendorfer@in.tum.de)
// May 24. 1998 - List of times introduced, and some bugs are fixed. (sven)
// May 23. 1998 - Removed static pointer - you can have more instances.
// It was Needed for KRegistry. KDirWatch now emits signals and doesn't
// call (or need) KFM. No more URL's - just plain paths. (sven)
// Mar 29. 1998 - added docs, stop/restart for particular Dirs and
// deep copies for list of dirs. (sven)
// Mar 28. 1998 - Created. (sven)
//US #include <config.h>
#ifdef HAVE_DNOTIFY
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#endif
#include <sys/stat.h>
#include <assert.h>
#include <qdir.h>
#include <qfile.h>
-#include <qintdict.h>
-#include <qptrlist.h>
+#include <q3intdict.h>
+#include <q3ptrlist.h>
#include <qsocketnotifier.h>
#include <qstringlist.h>
#include <qtimer.h>
+//Added by qt3to4:
+#include <Q3CString>
#include <kapplication.h>
#include <kdebug.h>
#include <kconfig.h>
#include <kconfigbase.h>
#include <kglobal.h>
#include <kstaticdeleter.h>
#include "kdirwatch.h"
#include "kdirwatch_p.h"
//US #include "global.h" // KIO::probably_slow_mounted
#define NO_NOTIFY (time_t) 0
static KDirWatchPrivate* dwp_self = 0;
#ifdef HAVE_DNOTIFY
#include <sys/utsname.h>
static int dnotify_signal = 0;
/* DNOTIFY signal handler
*
* As this is called asynchronously, only a flag is set and
* a rescan is requested.
* This is done by writing into a pipe to trigger a QSocketNotifier
* watching on this pipe: a timer is started and after a timeout,
* the rescan is done.
*/
void KDirWatchPrivate::dnotify_handler(int, siginfo_t *si, void *)
{
// write might change errno, we have to save it and restore it
// (Richard Stevens, Advanced programming in the Unix Environment)
int saved_errno = errno;
Entry* e = (dwp_self) ? dwp_self->fd_Entry.find(si->si_fd) :0;
// kdDebug(7001) << "DNOTIFY Handler: fd " << si->si_fd << " path "
// << QString(e ? e->path:"unknown") << endl;
if(!e || e->dn_fd != si->si_fd) {
qDebug("fatal error in KDirWatch");
} else
e->dn_dirty = true;
char c = 0;
write(dwp_self->mPipe[1], &c, 1);
errno = saved_errno;
}
static struct sigaction old_sigio_act;
/* DNOTIFY SIGIO signal handler
*
* When the kernel queue for the dnotify_signal overflows, a SIGIO is send.
*/
void KDirWatchPrivate::dnotify_sigio_handler(int sig, siginfo_t *si, void *p)
{
// write might change errno, we have to save it and restore it
// (Richard Stevens, Advanced programming in the Unix Environment)
int saved_errno = errno;
if (dwp_self)
dwp_self->rescan_all = true;
char c = 0;
write(dwp_self->mPipe[1], &c, 1);
errno = saved_errno;
// Call previous signal handler
if (old_sigio_act.sa_flags & SA_SIGINFO)
{
if (old_sigio_act.sa_sigaction)
(*old_sigio_act.sa_sigaction)(sig, si, p);
}
else
{
if ((old_sigio_act.sa_handler != SIG_DFL) &&
(old_sigio_act.sa_handler != SIG_IGN))
(*old_sigio_act.sa_handler)(sig);
}
}
#endif
//
// Class KDirWatchPrivate (singleton)
//
/* All entries (files/directories) to be watched in the
* application (coming from multiple KDirWatch instances)
* are registered in a single KDirWatchPrivate instance.
*
* At the moment, the following methods for file watching
* are supported:
* - Polling: All files to be watched are polled regularly
* using stat (more precise: QFileInfo.lastModified()).
* The polling frequency is determined from global kconfig
* settings, defaulting to 500 ms for local directories
* and 5000 ms for remote mounts
* - FAM (File Alternation Monitor): first used on IRIX, SGI
* has ported this method to LINUX. It uses a kernel part
* (IMON, sending change events to /dev/imon) and a user
* level damon (fam), to which applications connect for
* notification of file changes. For NFS, the fam damon
* on the NFS server machine is used; if IMON is not built
* into the kernel, fam uses polling for local files.
* - DNOTIFY: In late LINUX 2.3.x, directory notification was
* introduced. By opening a directory, you can request for
* UNIX signals to be sent to the process when a directory
* is changed.
*/
KDirWatchPrivate::KDirWatchPrivate()
{
timer = new QTimer(this);
connect (timer, SIGNAL(timeout()), this, SLOT(slotRescan()));
freq = 3600000; // 1 hour as upper bound
statEntries = 0;
delayRemove = false;
m_ref = 0;
//US KConfigGroup config(KGlobal::config(), QCString("DirWatch"));
//US m_nfsPollInterval = config.readNumEntry("NFSPollInterval", 5000);
//US m_PollInterval = config.readNumEntry("PollInterval", 500);
KConfig *config = KGlobal::config();
- KConfigGroupSaver saver( config, QCString("DirWatch") );
+ KConfigGroupSaver saver( config, Q3CString("DirWatch") );
m_nfsPollInterval = config->readNumEntry("NFSPollInterval", 5000);
m_PollInterval = config->readNumEntry("PollInterval", 500);
QString available("Stat");
#ifdef HAVE_FAM
// It's possible that FAM server can't be started
if (FAMOpen(&fc) ==0) {
available += ", FAM";
use_fam=true;
sn = new QSocketNotifier( FAMCONNECTION_GETFD(&fc),
QSocketNotifier::Read, this);
connect( sn, SIGNAL(activated(int)),
this, SLOT(famEventReceived()) );
}
else {
kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl;
use_fam=false;
}
#endif
#ifdef HAVE_DNOTIFY
supports_dnotify = true; // not guilty until proven guilty
rescan_all = false;
struct utsname uts;
int major, minor, patch;
if (uname(&uts) < 0)
supports_dnotify = false; // *shrug*
else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
supports_dnotify = false; // *shrug*
else if( major * 1000000 + minor * 1000 + patch < 2004019 ) { // <2.4.19
kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl;
supports_dnotify = false;
}
if( supports_dnotify ) {
available += ", DNotify";
pipe(mPipe);
fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
mSn = new QSocketNotifier( mPipe[0], QSocketNotifier::Read, this);
connect(mSn, SIGNAL(activated(int)), this, SLOT(slotActivated()));
connect(&mTimer, SIGNAL(timeout()), this, SLOT(slotRescan()));
struct sigaction act;
act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART;
#endif
if( dnotify_signal == 0 )
dnotify_signal = SIGRTMIN + 8;
sigaction(dnotify_signal, &act, NULL);
act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
sigaction(SIGIO, &act, &old_sigio_act);
}
#endif
kdDebug(7001) << "Available methods: " << available << endl;
}
/* This should never be called, but doesn't harm */
KDirWatchPrivate::~KDirWatchPrivate()
{
timer->stop();
/* remove all entries being watched */
removeEntries(0);
#ifdef HAVE_FAM
if (use_fam) {
FAMClose(&fc);
kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl;
}
#endif
}
#ifdef HAVE_DNOTIFY
void KDirWatchPrivate::slotActivated()
{
char dummy_buf[100];
read(mPipe[0], &dummy_buf, 100);
if (!mTimer.isActive())
mTimer.start(200, true);
}
/* In DNOTIFY mode, only entries which are marked dirty are scanned.
* We first need to mark all yet nonexistant, but possible created
* entries as dirty...
*/
void KDirWatchPrivate::Entry::propagate_dirty()
{
Entry* sub_entry;
for(sub_entry = m_entries.first(); sub_entry; sub_entry = m_entries.next())
{
if (!sub_entry->dn_dirty)
{
sub_entry->dn_dirty = true;
sub_entry->propagate_dirty();
}
}
}
#else // !HAVE_DNOTIFY
// slots always have to be defined...
void KDirWatchPrivate::slotActivated() {}
#endif
/* A KDirWatch instance is interested in getting events for
* this file/Dir entry.
*/
void KDirWatchPrivate::Entry::addClient(KDirWatch* instance)
{
Client* client = m_clients.first();
for(;client; client = m_clients.next())
if (client->instance == instance) break;
if (client) {
client->count++;
return;
}
client = new Client;
client->instance = instance;
client->count = 1;
client->watchingStopped = instance->isStopped();
client->pending = NoChange;
m_clients.append(client);
}
void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance)
{
Client* client = m_clients.first();
for(;client; client = m_clients.next())
if (client->instance == instance) break;
if (client) {
client->count--;
if (client->count == 0) {
m_clients.removeRef(client);
delete client;
}
}
}
/* get number of clients */
int KDirWatchPrivate::Entry::clients()
{
int clients = 0;
Client* client = m_clients.first();
for(;client; client = m_clients.next())
clients += client->count;
return clients;
}
KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const QString& _path)
{
// we only support absolute paths
if (_path.left(1) != "/") {
return 0;
}
QString path = _path;
if ( path.length() > 1 && path.right(1) == "/" )
path.truncate( path.length() - 1 );
EntryMap::Iterator it = m_mapEntries.find( path );
if ( it == m_mapEntries.end() )
return 0;
else
return &(*it);
}
// set polling frequency for a entry and adjust global freq if needed
void KDirWatchPrivate::useFreq(Entry* e, int newFreq)
{
e->freq = newFreq;
// a reasonable frequency for the global polling timer
if (e->freq < freq) {
freq = e->freq;
if (timer->isActive()) timer->changeInterval(freq);
kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl;
}
}
#if defined(HAVE_FAM)
// setup FAM notification, returns false if not possible
bool KDirWatchPrivate::useFAM(Entry* e)
{
if (!use_fam) return false;
e->m_mode = FAMMode;
if (e->isDir) {
if (e->m_status == NonExistent) {
// If the directory does not exist we watch the parent directory
addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
}
else {
int res =FAMMonitorDirectory(&fc, QFile::encodeName(e->path),
&(e->fr), e);
if (res<0) {
e->m_mode = UnknownMode;
use_fam=false;
return false;
}
kdDebug(7001) << " Setup FAM (Req "
<< FAMREQUEST_GETREQNUM(&(e->fr))
<< ") for " << e->path << endl;
}
}
else {
if (e->m_status == NonExistent) {
// If the file does not exist we watch the directory
addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
}
else {
int res = FAMMonitorFile(&fc, QFile::encodeName(e->path),
&(e->fr), e);
if (res<0) {
e->m_mode = UnknownMode;
use_fam=false;
return false;
}
kdDebug(7001) << " Setup FAM (Req "
<< FAMREQUEST_GETREQNUM(&(e->fr))
<< ") for " << e->path << endl;
}
}
// handle FAM events to avoid deadlock
// (FAM sends back all files in a directory when monitoring)
famEventReceived();
return true;
}
#endif
#ifdef HAVE_DNOTIFY
// setup DNotify notification, returns false if not possible
bool KDirWatchPrivate::useDNotify(Entry* e)
{
e->dn_fd = 0;
if (!supports_dnotify) return false;
e->m_mode = DNotifyMode;
if (e->isDir) {
e->dn_dirty = false;
if (e->m_status == Normal) {
int fd = open(QFile::encodeName(e->path).data(), O_RDONLY);
// Migrate fd to somewhere above 128. Some libraries have
// constructs like:
// fd = socket(...)
// if (fd > ARBITRARY_LIMIT)
// return error;
//
// Since programs might end up using a lot of KDirWatch objects
// for a rather long time the above braindamage could get
// triggered.
//
// By moving the kdirwatch fd's to > 128, calls like socket() will keep
// returning fd's < ARBITRARY_LIMIT for a bit longer.
int fd2 = fcntl(fd, F_DUPFD, 128);
if (fd2 >= 0)
{
close(fd);
fd = fd2;
}
if (fd<0) {
e->m_mode = UnknownMode;
return false;
}
int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
// if dependant is a file watch, we check for MODIFY & ATTRIB too
for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
fcntl(fd, F_NOTIFY, mask) < 0) {
kdDebug(7001) << "Not using Linux Directory Notifications."
<< endl;
supports_dnotify = false;
::close(fd);
e->m_mode = UnknownMode;
return false;
}
fd_Entry.replace(fd, e);
e->dn_fd = fd;
kdDebug(7001) << " Setup DNotify (fd " << fd
<< ") for " << e->path << endl;
}
else { // NotExisting
addEntry(0, QDir::cleanDirPath(e->path+"/.."), e, true);
}
}
else { // File
// we always watch the directory (DNOTIFY can't watch files alone)
// this notifies us about changes of files therein
addEntry(0, QFileInfo(e->path).dirPath(true), e, true);
}
return true;
}
#endif
bool KDirWatchPrivate::useStat(Entry* e)
{
//US we have no KIO::probably_slow_mounted. So disable this part
//US if (KIO::probably_slow_mounted(e->path))
//US useFreq(e, m_nfsPollInterval);
//US else
useFreq(e, m_PollInterval);
if (e->m_mode != StatMode) {
e->m_mode = StatMode;
statEntries++;
if ( statEntries == 1 ) {
// if this was first STAT entry (=timer was stopped)
timer->start(freq); // then start the timer
kdDebug(7001) << " Started Polling Timer, freq " << freq << endl;
}
}
kdDebug(7001) << " Setup Stat (freq " << e->freq
<< ") for " << e->path << endl;
return true;
}
/* If <instance> !=0, this KDirWatch instance wants to watch at <_path>,
* providing in <isDir> the type of the entry to be watched.
* Sometimes, entries are dependant on each other: if <sub_entry> !=0,
* this entry needs another entry to watch himself (when notExistent).
*/
void KDirWatchPrivate::addEntry(KDirWatch* instance, const QString& _path,
Entry* sub_entry, bool isDir)
{
QString path = _path;
if (path.startsWith("/dev/") || (path == "/dev"))
return; // Don't even go there.
if ( path.length() > 1 && path.right(1) == "/" )
path.truncate( path.length() - 1 );
EntryMap::Iterator it = m_mapEntries.find( path );
if ( it != m_mapEntries.end() )
{
if (sub_entry) {
(*it).m_entries.append(sub_entry);
kdDebug(7001) << "Added already watched Entry " << path
<< " (for " << sub_entry->path << ")" << endl;
#ifdef HAVE_DNOTIFY
Entry* e = &(*it);
if( e->dn_fd > 0 ) {
int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
// if dependant is a file watch, we check for MODIFY & ATTRIB too
for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) { // shouldn't happen
::close(e->dn_fd);
e->m_mode = UnknownMode;
fd_Entry.remove(e->dn_fd);
e->dn_fd = 0;
useStat( e );
}
}
#endif
}
else {
(*it).addClient(instance);
kdDebug(7001) << "Added already watched Entry " << path
<< " (now " << (*it).clients() << " clients)"
<< QString(" [%1]").arg(instance->name()) << endl;
}
return;
}
// we have a new path to watch
struct stat stat_buf;
bool exists = (stat(QFile::encodeName(path), &stat_buf) == 0);
Entry newEntry;
m_mapEntries.insert( path, newEntry );
// the insert does a copy, so we have to use <e> now
Entry* e = &(m_mapEntries[path]);
if (exists) {
QFileInfo fi ( path );
e->isDir = fi.isDir();
if (e->isDir && !isDir)
qWarning("KDirWatch: %s is a directory. Use addDir!", path.ascii());
else if (!e->isDir && isDir)
qWarning("KDirWatch: %s is a file. Use addFile!", path.ascii());
e->m_ctime = stat_buf.st_ctime;
e->m_status = Normal;
e->m_nlink = stat_buf.st_nlink;
}
else {
e->isDir = isDir;
e->m_ctime = invalid_ctime;
e->m_status = NonExistent;
e->m_nlink = 0;
}
e->path = path;
if (sub_entry)
e->m_entries.append(sub_entry);
else
e->addClient(instance);
kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path
<< (e->m_status == NonExistent ? " NotExisting" : "")
<< (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
<< (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
<< endl;
// now setup the notification method
e->m_mode = UnknownMode;
e->msecLeft = 0;
#if defined(HAVE_FAM)
if (useFAM(e)) return;
#endif
#ifdef HAVE_DNOTIFY
if (useDNotify(e)) return;
#endif
useStat(e);
}
void KDirWatchPrivate::removeEntry( KDirWatch* instance,
const QString& _path, Entry* sub_entry )
{
Entry* e = entry(_path);
if (!e) {
kdWarning(7001) << "KDirWatch::removeDir can't handle '" << _path << "'" << endl;
return;
}
if (sub_entry)
e->m_entries.removeRef(sub_entry);
else
e->removeClient(instance);
if (e->m_clients.count() || e->m_entries.count())
return;
if (delayRemove) {
// removeList is allowed to contain any entry at most once
if (removeList.findRef(e)==-1)
removeList.append(e);
// now e->isValid() is false
return;
}
#ifdef HAVE_FAM
if (e->m_mode == FAMMode) {
if ( e->m_status == Normal) {
FAMCancelMonitor(&fc, &(e->fr) );
kdDebug(7001) << "Cancelled FAM (Req "
<< FAMREQUEST_GETREQNUM(&(e->fr))
<< ") for " << e->path << endl;
}
else {
if (e->isDir)
removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
else
removeEntry(0, QFileInfo(e->path).dirPath(true), e);
}
}
#endif
#ifdef HAVE_DNOTIFY
if (e->m_mode == DNotifyMode) {
if (!e->isDir) {
removeEntry(0, QFileInfo(e->path).dirPath(true), e);
}
else { // isDir
// must close the FD.
if ( e->m_status == Normal) {
if (e->dn_fd) {
::close(e->dn_fd);
fd_Entry.remove(e->dn_fd);
kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
<< ") for " << e->path << endl;
e->dn_fd = 0;
}
}
else {
removeEntry(0, QDir::cleanDirPath(e->path+"/.."), e);
}
}
}
#endif
if (e->m_mode == StatMode) {
statEntries--;
if ( statEntries == 0 ) {
timer->stop(); // stop timer if lists are empty
kdDebug(7001) << " Stopped Polling Timer" << endl;
}
}
kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
<< (sub_entry ? QString(" for %1").arg(sub_entry->path) : QString(""))
<< (instance ? QString(" [%1]").arg(instance->name()) : QString(""))
<< endl;
m_mapEntries.remove( e->path ); // <e> not valid any more
}
/* Called from KDirWatch destructor:
* remove <instance> as client from all entries
*/
void KDirWatchPrivate::removeEntries( KDirWatch* instance )
{
- QPtrList<Entry> list;
+ Q3PtrList<Entry> list;
int minfreq = 3600000;
// put all entries where instance is a client in list
EntryMap::Iterator it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it ) {
Client* c = (*it).m_clients.first();
for(;c;c=(*it).m_clients.next())
if (c->instance == instance) break;
if (c) {
c->count = 1; // forces deletion of instance as client
list.append(&(*it));
}
else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
minfreq = (*it).freq;
}
for(Entry* e=list.first();e;e=list.next())
removeEntry(instance, e->path, 0);
if (minfreq > freq) {
// we can decrease the global polling frequency
freq = minfreq;
if (timer->isActive()) timer->changeInterval(freq);
kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
}
}
// instance ==0: stop scanning for all instances
bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
{
int stillWatching = 0;
Client* c = e->m_clients.first();
for(;c;c=e->m_clients.next()) {
if (!instance || instance == c->instance)
c->watchingStopped = true;
else if (!c->watchingStopped)
stillWatching += c->count;
}
kdDebug(7001) << instance->name() << " stopped scanning " << e->path
<< " (now " << stillWatching << " watchers)" << endl;
if (stillWatching == 0) {
// if nobody is interested, we don't watch
e->m_ctime = invalid_ctime; // invalid
// e->m_status = Normal;
}
return true;
}
// instance ==0: start scanning for all instances
bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
bool notify)
{
int wasWatching = 0, newWatching = 0;
Client* c = e->m_clients.first();
for(;c;c=e->m_clients.next()) {
if (!c->watchingStopped)
wasWatching += c->count;
else if (!instance || instance == c->instance) {
c->watchingStopped = false;
newWatching += c->count;
}
}
if (newWatching == 0)
return false;
kdDebug(7001) << instance->name() << " restarted scanning " << e->path
<< " (now " << wasWatching+newWatching << " watchers)" << endl;
// restart watching and emit pending events
int ev = NoChange;
if (wasWatching == 0) {
if (!notify) {
struct stat stat_buf;
bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0);
if (exists) {
e->m_ctime = stat_buf.st_ctime;
e->m_status = Normal;
e->m_nlink = stat_buf.st_nlink;
}
else {
e->m_ctime = invalid_ctime;
e->m_status = NonExistent;
e->m_nlink = 0;
}
}
e->msecLeft = 0;
ev = scanEntry(e);
}
emitEvent(e,ev);
return true;
}
// instance ==0: stop scanning for all instances
void KDirWatchPrivate::stopScan(KDirWatch* instance)
{
EntryMap::Iterator it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it )
stopEntryScan(instance, &(*it));
}
void KDirWatchPrivate::startScan(KDirWatch* instance,
bool notify, bool skippedToo )
{
if (!notify)
resetList(instance,skippedToo);
EntryMap::Iterator it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it )
restartEntryScan(instance, &(*it), notify);
// timer should still be running when in polling mode
}
// clear all pending events, also from stopped
void KDirWatchPrivate::resetList( KDirWatch* /*instance*/,
bool skippedToo )
{
EntryMap::Iterator it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it ) {
Client* c = (*it).m_clients.first();
for(;c;c=(*it).m_clients.next())
if (!c->watchingStopped || skippedToo)
c->pending = NoChange;
}
}
// Return event happened on <e>
//
int KDirWatchPrivate::scanEntry(Entry* e)
{
#ifdef HAVE_FAM
// we do not stat entries using FAM
if (e->m_mode == FAMMode) return NoChange;
#endif
// Shouldn't happen: Ignore "unknown" notification method
if (e->m_mode == UnknownMode) return NoChange;
#ifdef HAVE_DNOTIFY
if (e->m_mode == DNotifyMode) {
// we know nothing has changed, no need to stat
if(!e->dn_dirty) return NoChange;
e->dn_dirty = false;
}
#endif
if (e->m_mode == StatMode) {
// only scan if timeout on entry timer happens;
// e.g. when using 500msec global timer, a entry
// with freq=5000 is only watched every 10th time
e->msecLeft -= freq;
if (e->msecLeft>0) return NoChange;
e->msecLeft += e->freq;
}
struct stat stat_buf;
bool exists = (stat(QFile::encodeName(e->path), &stat_buf) == 0);
if (exists) {
if (e->m_status == NonExistent) {
e->m_ctime = stat_buf.st_ctime;
e->m_status = Normal;
e->m_nlink = stat_buf.st_nlink;
return Created;
}
if ( (e->m_ctime != invalid_ctime) &&
((stat_buf.st_ctime != e->m_ctime) ||
// (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
(stat_buf.st_nlink != e->m_nlink)) ) {
e->m_ctime = stat_buf.st_ctime;
e->m_nlink = stat_buf.st_nlink;
return Changed;
}
return NoChange;
}
// dir/file doesn't exist
if (e->m_ctime == invalid_ctime)
return NoChange;
e->m_ctime = invalid_ctime;
e->m_nlink = 0;
e->m_status = NonExistent;
return Deleted;
}
/* Notify all interested KDirWatch instances about a given event on an entry
* and stored pending events. When watching is stopped, the event is
* added to the pending events.
*/
void KDirWatchPrivate::emitEvent(Entry* e, int event, const QString &fileName)
{
QString path = e->path;
if (!fileName.isEmpty()) {
if (fileName[0] == '/')
path = fileName;
else
path += "/" + fileName;
}
Client* c = e->m_clients.first();
for(;c;c=e->m_clients.next()) {
if (c->instance==0 || c->count==0) continue;
if (c->watchingStopped) {
// add event to pending...
if (event == Changed)
c->pending |= event;
else if (event == Created || event == Deleted)
c->pending = event;
continue;
}
// not stopped
if (event == NoChange || event == Changed)
event |= c->pending;
c->pending = NoChange;
if (event == NoChange) continue;
if (event & Deleted) {
c->instance->setDeleted(path);
// emit only Deleted event...
continue;
}
if (event & Created) {
c->instance->setCreated(path);
// possible emit Change event after creation
}
if (event & Changed)
c->instance->setDirty(path);
}
}
// Remove entries which were marked to be removed
void KDirWatchPrivate::slotRemoveDelayed()
{
Entry* e;
delayRemove = false;
for(e=removeList.first();e;e=removeList.next())
removeEntry(0, e->path, 0);
removeList.clear();
}
/* Scan all entries to be watched for changes. This is done regularly
* when polling and once after a DNOTIFY signal. This is NOT used by FAM.
*/
void KDirWatchPrivate::slotRescan()
{
EntryMap::Iterator it;
// People can do very long things in the slot connected to dirty(),
// like showing a message box. We don't want to keep polling during
// that time, otherwise the value of 'delayRemove' will be reset.
bool timerRunning = timer->isActive();
if ( timerRunning )
timer->stop();
// We delay deletions of entries this way.
// removeDir(), when called in slotDirty(), can cause a crash otherwise
delayRemove = true;
#ifdef HAVE_DNOTIFY
- QPtrList<Entry> dList, cList;
+ Q3PtrList<Entry> dList, cList;
// for DNotify method,
if (rescan_all)
{
// mark all as dirty
it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it )
(*it).dn_dirty = true;
rescan_all = false;
}
else
{
// progate dirty flag to dependant entries (e.g. file watches)
it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it )
if ( ((*it).m_mode == DNotifyMode) && (*it).dn_dirty )
(*it).propagate_dirty();
}
#endif
it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it ) {
// we don't check invalid entries (i.e. remove delayed)
if (!(*it).isValid()) continue;
int ev = scanEntry( &(*it) );
#ifdef HAVE_DNOTIFY
if ((*it).m_mode == DNotifyMode) {
if ((*it).isDir && (ev == Deleted)) {
dList.append( &(*it) );
// must close the FD.
if ((*it).dn_fd) {
::close((*it).dn_fd);
fd_Entry.remove((*it).dn_fd);
(*it).dn_fd = 0;
}
}
else if ((*it).isDir && (ev == Created)) {
// For created, but yet without DNOTIFYing ...
if ( (*it).dn_fd == 0) {
cList.append( &(*it) );
if (! useDNotify( &(*it) )) {
// if DNotify setup fails...
useStat( &(*it) );
}
}
}
}
#endif
if ( ev != NoChange )
emitEvent( &(*it), ev);
}
#ifdef HAVE_DNOTIFY
// Scan parent of deleted directories for new creation
Entry* e;
for(e=dList.first();e;e=dList.next())
addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
// Remove watch of parent of new created directories
for(e=cList.first();e;e=cList.next())
removeEntry(0, QDir::cleanDirPath( e->path+"/.."), e);
#endif
if ( timerRunning )
timer->start(freq);
QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
}
#ifdef HAVE_FAM
void KDirWatchPrivate::famEventReceived()
{
static FAMEvent fe;
delayRemove = true;
while(use_fam && FAMPending(&fc)) {
if (FAMNextEvent(&fc, &fe) == -1) {
kdWarning(7001) << "FAM connection problem, switching to polling."
<< endl;
use_fam = false;
delete sn; sn = 0;
// Replace all FAMMode entries with DNotify/Stat
EntryMap::Iterator it;
it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it )
if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
#ifdef HAVE_DNOTIFY
if (useDNotify( &(*it) )) continue;
#endif
useStat( &(*it) );
}
}
else
checkFAMEvent(&fe);
}
QTimer::singleShot(0, this, SLOT(slotRemoveDelayed()));
}
void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
{
// Don't be too verbose ;-)
if ((fe->code == FAMExists) ||
(fe->code == FAMEndExist) ||
(fe->code == FAMAcknowledge)) return;
// $HOME/.X.err grows with debug output, so don't notify change
if ( *(fe->filename) == '.') {
if (strncmp(fe->filename, ".X.err", 6) == 0) return;
if (strncmp(fe->filename, ".xsession-errors", 16) == 0) return;
}
Entry* e = 0;
EntryMap::Iterator it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it )
if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
e = &(*it);
break;
}
// Entry* e = static_cast<Entry*>(fe->userdata);
kdDebug(7001) << "Processing FAM event ("
<< ((fe->code == FAMChanged) ? "FAMChanged" :
(fe->code == FAMDeleted) ? "FAMDeleted" :
(fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
(fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
(fe->code == FAMCreated) ? "FAMCreated" :
(fe->code == FAMMoved) ? "FAMMoved" :
(fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
(fe->code == FAMExists) ? "FAMExists" :
(fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
<< ", " << fe->filename
<< ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
<< ")" << endl;
if (!e) {
// this happens e.g. for FAMAcknowledge after deleting a dir...
// kdDebug(7001) << "No entry for FAM event ?!" << endl;
return;
}
if (e->m_status == NonExistent) {
kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl;
return;
}
if (e->isDir)
switch (fe->code)
{
case FAMDeleted:
// file absolute: watched dir
if (fe->filename[0] == '/')
{
// a watched directory was deleted
e->m_status = NonExistent;
FAMCancelMonitor(&fc, &(e->fr) ); // needed ?
kdDebug(7001) << "Cancelled FAMReq "
<< FAMREQUEST_GETREQNUM(&(e->fr))
<< " for " << e->path << endl;
// Scan parent for a new creation
addEntry(0, QDir::cleanDirPath( e->path+"/.."), e, true);
}
emitEvent(e, Deleted, QFile::decodeName(fe->filename));
break;
case FAMCreated: {
// check for creation of a directory we have to watch
Entry *sub_entry = e->m_entries.first();
for(;sub_entry; sub_entry = e->m_entries.next())
if (sub_entry->path == e->path + "/" + fe->filename) break;
if (sub_entry && sub_entry->isDir) {
QString path = e->path;
removeEntry(0,e->path,sub_entry); // <e> can be invalid here!!
sub_entry->m_status = Normal;
if (!useFAM(sub_entry))
useStat(sub_entry);
emitEvent(sub_entry, Created);
}
else emitEvent(e, Created, QFile::decodeName(fe->filename));
break;
}
case FAMChanged:
emitEvent(e, Changed, QFile::decodeName(fe->filename));
default:
break;
}
else switch (fe->code)
{
case FAMCreated: emitEvent(e, Created);
break;
case FAMDeleted: emitEvent(e, Deleted);
break;
case FAMChanged: emitEvent(e, Changed);
break;
default: break;
}
}
#else
void KDirWatchPrivate::famEventReceived() {}
#endif
void KDirWatchPrivate::statistics()
{
EntryMap::Iterator it;
kdDebug(7001) << "Entries watched:" << endl;
if (m_mapEntries.count()==0) {
kdDebug(7001) << " None." << endl;
}
else {
it = m_mapEntries.begin();
for( ; it != m_mapEntries.end(); ++it ) {
Entry* e = &(*it);
kdDebug(7001) << " " << e->path << " ("
<< ((e->m_status==Normal)?"":"Nonexistent ")
<< (e->isDir ? "Dir":"File") << ", using "
<< ((e->m_mode == FAMMode) ? "FAM" :
(e->m_mode == DNotifyMode) ? "DNotify" :
(e->m_mode == StatMode) ? "Stat" : "Unknown Method")
<< ")" << endl;
Client* c = e->m_clients.first();
for(;c; c = e->m_clients.next()) {
QString pending;
if (c->watchingStopped) {
if (c->pending & Deleted) pending += "deleted ";
if (c->pending & Created) pending += "created ";
if (c->pending & Changed) pending += "changed ";
if (!pending.isEmpty()) pending = " (pending: " + pending + ")";
pending = ", stopped" + pending;
}
kdDebug(7001) << " by " << c->instance->name()
<< " (" << c->count << " times)"
<< pending << endl;
}
if (e->m_entries.count()>0) {
kdDebug(7001) << " dependent entries:" << endl;
Entry* d = e->m_entries.first();
for(;d; d = e->m_entries.next()) {
kdDebug(7001) << " " << d->path << endl;
}
}
}
}
}
//
// Class KDirWatch
//
static KStaticDeleter<KDirWatch> sd_dw;
KDirWatch* KDirWatch::s_pSelf = 0L;
KDirWatch* KDirWatch::self()
{
if ( !s_pSelf ) {
//US sd_dw.setObject( s_pSelf, new KDirWatch );
s_pSelf = sd_dw.setObject( new KDirWatch );
}
return s_pSelf;
}
bool KDirWatch::exists()
{
return s_pSelf != 0;
}
KDirWatch::KDirWatch (QObject* parent, const char* name)
: QObject(parent,name)
{
if (!name) {
static int nameCounter = 0;
nameCounter++;
setName(QString("KDirWatch-%1").arg(nameCounter).ascii());
}
if (!dwp_self)
dwp_self = new KDirWatchPrivate;
d = dwp_self;
d->ref();
_isStopped = false;
}
KDirWatch::~KDirWatch()
{
if (d) d->removeEntries(this);
if ( d->deref() )
{
// delete it if it's the last one
delete d;
dwp_self = 0L;
}
}
// TODO: add watchFiles/recursive support
void KDirWatch::addDir( const QString& _path,
bool watchFiles, bool recursive)
{
if (watchFiles || recursive) {
kdDebug(7001) << "addDir - recursive/watchFiles not supported in KDE 3.0"
<< endl;
}
if (d) d->addEntry(this, _path, 0, true);
}
void KDirWatch::addFile( const QString& _path )
{
if (d) d->addEntry(this, _path, 0, false);
}
QDateTime KDirWatch::ctime( const QString &_path )
{
KDirWatchPrivate::Entry* e = d->entry(_path);
if (!e)
return QDateTime();
QDateTime result;
result.setTime_t(e->m_ctime);
return result;
}
void KDirWatch::removeDir( const QString& _path )
{
if (d) d->removeEntry(this, _path, 0);
}
void KDirWatch::removeFile( const QString& _path )
{
if (d) d->removeEntry(this, _path, 0);
}
bool KDirWatch::stopDirScan( const QString& _path )
{
if (d) {
KDirWatchPrivate::Entry *e = d->entry(_path);
if (e && e->isDir) return d->stopEntryScan(this, e);
}
return false;
}
bool KDirWatch::restartDirScan( const QString& _path )
{
if (d) {
KDirWatchPrivate::Entry *e = d->entry(_path);
if (e && e->isDir)
// restart without notifying pending events
return d->restartEntryScan(this, e, false);
}
return false;
}
void KDirWatch::stopScan()
{
if (d) d->stopScan(this);
_isStopped = true;
}
void KDirWatch::startScan( bool notify, bool skippedToo )
{
_isStopped = false;
if (d) d->startScan(this, notify, skippedToo);
}
diff --git a/microkde/kio/kio/kdirwatch_p.h b/microkde/kio/kio/kdirwatch_p.h
index 0ab482f..be74f2a 100644
--- a/microkde/kio/kio/kdirwatch_p.h
+++ b/microkde/kio/kio/kdirwatch_p.h
@@ -1,153 +1,153 @@
/* Private Header for class of KDirWatchPrivate
*
* this separate header file is needed for MOC processing
* because KDirWatchPrivate has signals and slots
*/
/*
Enhanced Version of the file for platform independent KDE tools.
Copyright (c) 2004 Ulf Schenk
$Id$
*/
#ifndef _KDIRWATCH_P_H
#define _KDIRWATCH_P_H
#ifdef HAVE_FAM
#include <fam.h>
#endif
-#include <qptrlist.h>
+#include <q3ptrlist.h>
#include <kdirwatch.h>
#include <ctime>
#define invalid_ctime ((time_t)-1)
/* KDirWatchPrivate is a singleton and does the watching
* for every KDirWatch instance in the application.
*/
class KDirWatchPrivate : public QObject
{
Q_OBJECT
public:
enum entryStatus { Normal = 0, NonExistent };
enum entryMode { UnknownMode = 0, StatMode, DNotifyMode, FAMMode };
enum { NoChange=0, Changed=1, Created=2, Deleted=4 };
struct Client {
KDirWatch* instance;
int count;
// did the instance stop watching
bool watchingStopped;
// events blocked when stopped
int pending;
};
class Entry
{
public:
// the last observed modification time
time_t m_ctime;
// the last observed link count
int m_nlink;
entryStatus m_status;
entryMode m_mode;
bool isDir;
// instances interested in events
- QPtrList<Client> m_clients;
+ Q3PtrList<Client> m_clients;
// nonexistent entries of this directory
- QPtrList<Entry> m_entries;
+ Q3PtrList<Entry> m_entries;
QString path;
int msecLeft, freq;
void addClient(KDirWatch*);
void removeClient(KDirWatch*);
int clients();
bool isValid() { return m_clients.count() || m_entries.count(); }
#ifdef HAVE_FAM
FAMRequest fr;
#endif
#ifdef HAVE_DNOTIFY
int dn_fd;
bool dn_dirty;
void propagate_dirty();
#endif
};
typedef QMap<QString,Entry> EntryMap;
KDirWatchPrivate();
~KDirWatchPrivate();
void resetList (KDirWatch*,bool);
void useFreq(Entry* e, int newFreq);
void addEntry(KDirWatch*,const QString&, Entry*, bool);
void removeEntry(KDirWatch*,const QString&, Entry*);
bool stopEntryScan(KDirWatch*, Entry*);
bool restartEntryScan(KDirWatch*, Entry*, bool );
void stopScan(KDirWatch*);
void startScan(KDirWatch*, bool, bool);
void removeEntries(KDirWatch*);
void statistics();
Entry* entry(const QString&);
int scanEntry(Entry* e);
void emitEvent(Entry* e, int event, const QString &fileName = QString::null);
// Memory management - delete when last KDirWatch gets deleted
void ref() { m_ref++; }
bool deref() { return ( --m_ref == 0 ); }
public slots:
void slotRescan();
void famEventReceived(); // for FAM
void slotActivated(); // for DNOTIFY
void slotRemoveDelayed();
public:
QTimer *timer;
EntryMap m_mapEntries;
private:
int freq;
int statEntries;
int m_nfsPollInterval, m_PollInterval;
int m_ref;
bool useStat(Entry*);
bool delayRemove;
- QPtrList<Entry> removeList;
+ Q3PtrList<Entry> removeList;
#ifdef HAVE_FAM
QSocketNotifier *sn;
FAMConnection fc;
bool use_fam;
void checkFAMEvent(FAMEvent*);
bool useFAM(Entry*);
#endif
#ifdef HAVE_DNOTIFY
bool supports_dnotify;
bool rescan_all;
int mPipe[2];
QTimer mTimer;
QSocketNotifier *mSn;
- QIntDict<Entry> fd_Entry;
+ Q3IntDict<Entry> fd_Entry;
static void dnotify_handler(int, siginfo_t *si, void *);
static void dnotify_sigio_handler(int, siginfo_t *si, void *);
bool useDNotify(Entry*);
#endif
};
#endif // KDIRWATCH_P_H