You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeaddons/konq-plugins/dirfilter/dirfilterplugin.cpp

560 lines
16 KiB

/*
Copyright (C) 2000, 2001, 2002 Dawit Alemayehu <adawit@kde.org>
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., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <unistd.h>
#include <sys/types.h>
#include <tqtimer.h>
#include <tqapplication.h>
#include <tqlabel.h>
#include <tqpushbutton.h>
#include <tqhbox.h>
#include <tqwhatsthis.h>
#include <tqiconview.h>
#include <klistview.h>
#include <kdebug.h>
#include <klocale.h>
#include <kinstance.h>
#include <kaction.h>
#include <kpopupmenu.h>
#include <kmessagebox.h>
#include <kiconloader.h>
#include <kdirlister.h>
#include <klistviewsearchline.h>
#include <kiconviewsearchline.h>
#include <konq_dirpart.h>
#include <konq_propsview.h>
#include <kstaticdeleter.h>
#include <kgenericfactory.h>
#include <kparts/browserextension.h>
#include "dirfilterplugin.h"
SessionManager* SessionManager::m_self = 0;
static KStaticDeleter<SessionManager> dirfiltersd;
SessionManager *SessionManager::self ()
{
if (!m_self)
m_self = dirfiltersd.setObject(m_self, new SessionManager);
return m_self;
}
SessionManager::SessionManager()
{
m_bSettingsLoaded = false;
loadSettings ();
}
SessionManager::~SessionManager()
{
saveSettings();
m_self = 0;
}
TQString SessionManager::generateKey (const KURL& url) const
{
TQString key;
key = url.protocol ();
key += ':';
if (!url.host ().isEmpty ())
{
key += url.host ();
key += ':';
}
key += url.path ();
key += ':';
key += TQString::number (m_pid);
return key;
}
TQStringList SessionManager::restoreMimeFilters (const KURL& url) const
{
TQString key(generateKey(url));
return m_filters[key];
}
TQString SessionManager::restoreTypedFilter (const KURL& url) const
{
TQString key(generateKey(url));
return m_typedFilter[key];
}
void SessionManager::save (const KURL& url, const TQStringList& filters)
{
TQString key = generateKey(url);
m_filters[key] = filters;
}
void SessionManager::save (const KURL& url, const TQString& typedFilter)
{
TQString key = generateKey(url);
m_typedFilter[key] = typedFilter;
}
void SessionManager::saveSettings()
{
KConfig cfg ("dirfilterrc", false, false);
cfg.setGroup ("General");
cfg.writeEntry ("ShowCount", showCount);
cfg.writeEntry ("UseMultipleFilters", useMultipleFilters);
cfg.sync();
}
void SessionManager::loadSettings()
{
if (m_bSettingsLoaded)
return;
KConfig cfg ("dirfilterrc", false, false);
cfg.setGroup ("General");
showCount = cfg.readBoolEntry ("ShowCount", false);
useMultipleFilters = cfg.readBoolEntry ("UseMultipleFilters", true);
m_pid = getpid ();
m_bSettingsLoaded = true;
}
DirFilterPlugin::DirFilterPlugin (TQObject* parent, const char* name,
const TQStringList&)
:KParts::Plugin (parent, name),
m_pFilterMenu(0),
m_searchWidget(0),
m_oldFilterString("")
{
m_part = ::tqqt_cast<KonqDirPart*>(parent);
if ( !m_part || !m_part->scrollWidget() )
return;
m_pFilterMenu = new KActionMenu (i18n("View F&ilter"), "filter",
actionCollection(), "filterdir");
m_pFilterMenu->setDelayed (false);
m_pFilterMenu->setWhatsThis(i18n("Allow to filter the currently displayed items by filetype."));
connect (m_pFilterMenu->popupMenu(), TQT_SIGNAL (aboutToShow()),
TQT_SLOT (slotShowPopup()));
connect (m_part, TQT_SIGNAL (itemRemoved(const KFileItem*)),
TQT_SLOT( slotItemRemoved (const KFileItem*)));
connect (m_part, TQT_SIGNAL (itemsAdded(const KFileItemList&)),
TQT_SLOT (slotItemsAdded(const KFileItemList&)));
connect (m_part, TQT_SIGNAL (itemsFilteredByMime(const KFileItemList&)),
TQT_SLOT (slotItemsAdded(const KFileItemList&)));
connect (m_part, TQT_SIGNAL(aboutToOpenURL()), TQT_SLOT(slotOpenURL()));
// add a searchline filter for konqis icons/list views
TQHBox *hbox = new TQHBox(m_part->widget());
hbox->hide();
KAction *clear = new KAction(i18n("Clear Filter Field"),
TQApplication::reverseLayout() ? "clear_left" : "locationbar_erase",
0, 0, 0, actionCollection(), "clear_filter");
clear->setWhatsThis(i18n("Clear filter field<p>Clears the content of the filter field."));
if ( ::tqqt_cast<KListView*>(m_part->scrollWidget()) )
{
m_searchWidget = new KListViewSearchLine(hbox);
static_cast<KListViewSearchLine*>(m_searchWidget)->setListView(static_cast<KListView*>(m_part->scrollWidget()));
}
else if ( ::tqqt_cast<TQIconView*>(m_part->scrollWidget()) )
{
m_searchWidget = new KIconViewSearchLine(hbox);
static_cast<KIconViewSearchLine*>(m_searchWidget)->setIconView(static_cast<TQIconView*>(m_part->scrollWidget()));
}
if ( m_searchWidget )
{
TQWhatsThis::add(m_searchWidget, i18n("Enter here a text which an item in the view must contain anywhere to be shown."));
connect(clear, TQT_SIGNAL(activated()), m_searchWidget, TQT_SLOT(clear()));
connect(m_searchWidget, TQT_SIGNAL(textChanged(const TQString&)), this, TQT_SLOT(searchTextChanged(const TQString&)));
}
KWidgetAction *filterAction = new KWidgetAction(hbox, i18n("Filter Field"),
0, 0, 0, actionCollection(), "toolbar_filter_field");
filterAction->setShortcutConfigurable(false);
// FIXME: This causes crashes for an unknown reason on certain systems
// Probably the iconview onchange event handler should tempoarily stop this timer before doing anything else
// Really the broken TQt3 iconview should just be modified to include a hidden list; it would make things *so* much easier!
m_refreshTimer = new TQTimer( this );
m_reactivateRefreshTimer = new TQTimer( this );
connect( m_refreshTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(activateSearch()) );
m_refreshTimer->start( 200, FALSE ); // 200 millisecond continuous timer
connect( m_reactivateRefreshTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(reactivateRefreshTimer()) );
}
DirFilterPlugin::~DirFilterPlugin()
{
m_reactivateRefreshTimer->stop();
m_refreshTimer->stop();
delete m_pFilterMenu;
delete m_refreshTimer;
delete m_reactivateRefreshTimer;
}
void DirFilterPlugin::searchTextChanged(const TQString& newtext)
{
m_refreshTimer->stop();
m_reactivateRefreshTimer->stop();
m_reactivateRefreshTimer->start( 1000, TRUE );
}
void DirFilterPlugin::reactivateRefreshTimer()
{
m_refreshTimer->start( 200, FALSE ); // 200 millisecond continuous time
}
void DirFilterPlugin::slotOpenURL ()
{
KURL url = m_part->url();
//kdDebug(90190) << "DirFilterPlugin: New URL : " << url.url() << endl;
//kdDebug(90190) << "DirFilterPlugin: Current URL: " << m_pURL.url() << endl;
if (m_pURL != url)
{
//Clears the hidden list which is by now outdated...
if (m_searchWidget)
{
SessionManager::self()->save(m_pURL, m_searchWidget->text());
m_searchWidget->clear();
TQString typedFilter(SessionManager::self()->restoreTypedFilter(url));
m_searchWidget->completionObject()->addItem(typedFilter);
m_searchWidget->setText(typedFilter);
}
m_pURL = url;
m_pMimeInfo.clear();
m_part->setMimeFilter (SessionManager::self()->restoreMimeFilters(url));
}
}
void DirFilterPlugin::slotShowPopup()
{
if (!m_part)
{
m_pFilterMenu->setEnabled (false);
return;
}
int id = 0;
uint enableReset = 0;
TQString label;
TQStringList inodes;
m_pFilterMenu->popupMenu()->clear();
m_pFilterMenu->popupMenu()->insertTitle (i18n("Only Show Items of Type"));
MimeInfoIterator it = m_pMimeInfo.begin();
const MimeInfoIterator end = m_pMimeInfo.end();
for (; it != end ; ++it)
{
if (it.key().startsWith("inode"))
{
inodes << it.key();
continue;
}
if (!SessionManager::self()->showCount)
label = it.data().mimeComment;
else
{
label = it.data().mimeComment;
label += " (";
label += TQString::number (it.data().filenames.size ());
label += ")";
}
m_pMimeInfo[it.key()].id = m_pFilterMenu->popupMenu()->insertItem (
SmallIconSet(it.data().iconName), label,
this, TQT_SLOT(slotItemSelected(int)), 0, ++id);
if (it.data().useAsFilter)
{
m_pFilterMenu->popupMenu()->setItemChecked (id, true);
enableReset++;
}
}
// Add all the items that have mime-type of "inode/*" here...
if (!inodes.isEmpty())
{
m_pFilterMenu->popupMenu()->insertSeparator ();
TQStringList::Iterator it = inodes.begin();
TQStringList::Iterator end = inodes.end();
for (; it != end; ++it)
{
if (!SessionManager::self()->showCount)
label = m_pMimeInfo[(*it)].mimeComment;
else
{
label = m_pMimeInfo[(*it)].mimeComment;
label += " (";
label += TQString::number (m_pMimeInfo[(*it)].filenames.size ());
label += ")";
}
m_pMimeInfo[(*it)].id = m_pFilterMenu->popupMenu()->insertItem (
SmallIconSet(m_pMimeInfo[(*it)].iconName), label,
this, TQT_SLOT(slotItemSelected(int)), 0, ++id);
if (m_pMimeInfo[(*it)].useAsFilter)
{
m_pFilterMenu->popupMenu()->setItemChecked (id, true);
enableReset ++;
}
}
}
m_pFilterMenu->popupMenu()->insertSeparator ();
id = m_pFilterMenu->popupMenu()->insertItem (i18n("Use Multiple Filters"),
this, TQT_SLOT(slotMultipleFilters()));
m_pFilterMenu->popupMenu()->setItemEnabled (id, enableReset <= 1);
m_pFilterMenu->popupMenu()->setItemChecked (id, SessionManager::self()->useMultipleFilters);
id = m_pFilterMenu->popupMenu()->insertItem (i18n("Show Count"), this,
TQT_SLOT(slotShowCount()));
m_pFilterMenu->popupMenu()->setItemChecked (id, SessionManager::self()->showCount);
id = m_pFilterMenu->popupMenu()->insertItem (i18n("Reset"), this,
TQT_SLOT(slotReset()));
m_pFilterMenu->popupMenu()->setItemEnabled (id, enableReset);
}
void DirFilterPlugin::slotItemSelected (int id)
{
if (!m_part)
return;
MimeInfoIterator it = m_pMimeInfo.begin();
while (it != m_pMimeInfo.end () && id != it.data().id)
it++;
if (it != m_pMimeInfo.end())
{
TQStringList filters;
if (it.data().useAsFilter)
{
it.data().useAsFilter = false;
filters = m_part->mimeFilter ();
if (filters.remove (it.key()))
m_part->setMimeFilter (filters);
}
else
{
m_pMimeInfo[it.key()].useAsFilter = true;
if (SessionManager::self()->useMultipleFilters)
{
filters = m_part->mimeFilter ();
filters << it.key();
}
else
{
filters << it.key();
MimeInfoIterator item = m_pMimeInfo.begin();
while ( item != m_pMimeInfo.end() )
{
if ( item != it )
item.data().useAsFilter = false;
item++;
}
}
m_part->setMimeFilter (filters);
}
KURL url = m_part->url();
m_part->openURL (url);
SessionManager::self()->save (url, filters);
}
}
void DirFilterPlugin::slotItemsAdded (const KFileItemList& list)
{
KURL url = m_part->url();
if (list.count() == 0 || !m_part || !m_part->nameFilter().isEmpty())
{
m_pFilterMenu->setEnabled (m_part->nameFilter().isEmpty());
return;
}
if ( ::tqqt_cast<KListView*>(m_part->scrollWidget()) )
{
static_cast<KListViewSearchLine*>(m_searchWidget)->updateSearch();
}
else if ( ::tqqt_cast<TQIconView*>(m_part->scrollWidget()) )
{
static_cast<KIconViewSearchLine*>(m_searchWidget)->updateSearch();
}
// Make sure the filter menu is enabled once a named
// filter is removed.
if (!m_pFilterMenu->isEnabled())
m_pFilterMenu->setEnabled (true);
for (KFileItemListIterator it (list); it.current (); ++it)
{
TQString name = it.current()->name();
KMimeType::Ptr mime = it.current()->mimeTypePtr(); // don't resolve mimetype if unknown
if (!mime)
continue;
TQString mimeType = mime->name();
if (!m_pMimeInfo.contains (mimeType))
{
MimeInfo& mimeInfo = m_pMimeInfo[mimeType];
TQStringList filters = m_part->mimeFilter ();
mimeInfo.useAsFilter = (!filters.isEmpty () && filters.contains (mimeType));
mimeInfo.mimeComment = mime->comment();
mimeInfo.iconName = mime->icon(KURL(), false);
mimeInfo.filenames.insert(name, false);
}
else
{
m_pMimeInfo[mimeType].filenames.insert(name, false);
}
}
}
void DirFilterPlugin::slotItemRemoved (const KFileItem* item)
{
if (!item || !m_part)
return;
// Due to the poor implementation of qiconviewitem, there is no way for it to know if an item on the hidden list was deleted
// This *will* eventually cause the kiconviewsearchline to attempt to reference a deleted object, resulting in a crash
// This is illustrated well with two Konqueror windows. Open one, set the filter bar to filter out a file, then delete the filtered file from another window.
// Now clear the search bar (forcing injection of the deleted iconview object pointer from the hidden list). Konqueror will crash!
// If iconDeleted is called, it allows kiconviewsearchline to temporarily add the affected qiconviewitem(s) (by filename) to the iconview so that as far as
// qiconview is concerned the qiconviewitem objects never left.
// NOTE: This bug is NOT present in qlistviewitem
// HACK around it here...
if ( ::tqqt_cast<TQIconView*>(m_part->scrollWidget()) ) {
static_cast<KIconViewSearchLine*>(m_searchWidget)->iconDeleted(item->name());
}
TQString mimeType = item->mimetype().stripWhiteSpace();
if (m_pMimeInfo.contains (mimeType)) {
MimeInfo info = m_pMimeInfo [mimeType];
if (info.filenames.size () > 1)
m_pMimeInfo [mimeType].filenames.remove (item->name ());
else {
if (info.useAsFilter) {
TQStringList filters = m_part->mimeFilter ();
filters.remove (mimeType);
m_part->setMimeFilter (filters);
SessionManager::self()->save (m_part->url(), filters);
TQTimer::singleShot( 0, this, TQT_SLOT(slotTimeout()) );
}
m_pMimeInfo.remove (mimeType);
}
}
}
void DirFilterPlugin::activateSearch()
{
// FIXME: If any of the files change while they are hidden in iconview mode, they will
// reappear under the wrong (old) name. This routine was originally intended to fix that
// problem, but will need to be able to detect when a change has occurred to be effective.
if (!m_searchWidget)
return;
if (m_oldFilterString == m_searchWidget->text())
return;
m_oldFilterString = m_searchWidget->text();
if ( ::tqqt_cast<KListView*>(m_part->scrollWidget()) ) {
static_cast<KListViewSearchLine*>(m_searchWidget)->updateSearch();
}
else if ( ::tqqt_cast<TQIconView*>(m_part->scrollWidget()) ) {
static_cast<KIconViewSearchLine*>(m_searchWidget)->updateSearch();
}
}
void DirFilterPlugin::slotReset()
{
if (!m_part)
return;
MimeInfoIterator it = m_pMimeInfo.begin();
for (; it != m_pMimeInfo.end(); ++it)
it.data().useAsFilter = false;
TQStringList filters;
KURL url = m_part->url();
m_part->setMimeFilter (filters);
SessionManager::self()->save (url, filters);
m_part->openURL (url);
}
void DirFilterPlugin::slotShowCount()
{
if (SessionManager::self()->showCount)
SessionManager::self()->showCount = false;
else
SessionManager::self()->showCount = true;
}
void DirFilterPlugin::slotMultipleFilters()
{
if (SessionManager::self()->useMultipleFilters)
SessionManager::self()->useMultipleFilters = false;
else
SessionManager::self()->useMultipleFilters = true;
}
void DirFilterPlugin::slotTimeout()
{
if (m_part)
m_part->openURL (m_part->url());
}
typedef KGenericFactory<DirFilterPlugin> DirFilterFactory;
K_EXPORT_COMPONENT_FACTORY (libdirfilterplugin, DirFilterFactory("dirfilterplugin"))
#include "dirfilterplugin.moc"