/* Copyright (C) 2000, 2001, 2002 Dawit Alemayehu 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dirfilterplugin.h" SessionManager* SessionManager::m_self = 0; static KStaticDeleter 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() { TDEConfig cfg ("dirfilterrc", false, false); cfg.setGroup ("General"); cfg.writeEntry ("ShowCount", showCount); cfg.writeEntry ("UseMultipleFilters", useMultipleFilters); cfg.sync(); } void SessionManager::loadSettings() { if (m_bSettingsLoaded) return; TDEConfig 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_oldFilterString("") { m_part = ::tqqt_cast(parent); if ( !m_part || !m_part->scrollWidget() ) return; m_pFilterMenu = new TDEActionMenu (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(itemsRefresh(const KFileItemList&)), TQT_SLOT (slotItemsRefresh(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(); TDEAction *clear = new TDEAction(i18n("Clear Filter Field"), TQApplication::reverseLayout() ? "clear_left" : "locationbar_erase", 0, 0, 0, actionCollection(), "clear_filter"); clear->setWhatsThis(i18n("Clear filter field

Clears the content of the filter field.")); if ( ::tqqt_cast(m_part->scrollWidget()) ) { m_searchWidget = new TDEListViewSearchLine(hbox); static_cast(m_searchWidget)->setListView(static_cast(m_part->scrollWidget())); } else if ( ::tqqt_cast(m_part->scrollWidget()) ) { m_searchWidget = new TDEIconViewSearchLine(hbox); static_cast(m_searchWidget)->setIconView(static_cast(m_part->scrollWidget())); } else { m_searchWidget=NULL; } 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(m_part->scrollWidget()) ) { static_cast(m_searchWidget)->updateSearch(); } else if ( ::tqqt_cast(m_part->scrollWidget()) ) { static_cast(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(m_part->scrollWidget()) ) { static_cast(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); // Use 1ms delay to avoid possible race conditions TQTimer::singleShot(1, this, TQT_SLOT(slotTimeout())); } m_pMimeInfo.remove(mimeType); } } } void DirFilterPlugin::slotItemsRefresh(const KFileItemList &list) { if (list.count() == 0 || !m_part || !m_part->nameFilter().isEmpty()) { m_pFilterMenu->setEnabled (m_part->nameFilter().isEmpty()); return; } if ( ::tqqt_cast(m_part->scrollWidget()) ) { static_cast(m_searchWidget)->updateSearch(); } else if ( ::tqqt_cast(m_part->scrollWidget()) ) { static_cast(m_searchWidget)->updateSearch(); } TQMap itemsMap; if (::tqqt_cast(m_part->scrollWidget())) { TQListView *listview = ::tqqt_cast(m_part->scrollWidget()); for (TQListViewItemIterator lv_it(listview); lv_it.current(); ++lv_it) { itemsMap.insert(lv_it.current()->text(0), false); } } else if ( ::tqqt_cast(m_part->scrollWidget()) ) { TQIconView *iconview = ::tqqt_cast(m_part->scrollWidget()); for (TQIconViewItem *item = iconview->firstItem(); item; item = item->nextItem()) { itemsMap.insert(item->text(), false); } } MimeInfoIterator it; for (it = m_pMimeInfo.begin(); it != m_pMimeInfo.end(); /*empty*/) { TQMap::iterator file_it; for ( file_it = it.data().filenames.begin(); file_it != it.data().filenames.end(); /*empty*/) { if (!itemsMap.contains(file_it.key())) { // Remove mimeInfo for this file (the file was problably renamed) TQMap::iterator del_fit = file_it; ++file_it; it.data().filenames.remove(del_fit); } else ++file_it; } if (it.data().filenames.size() == 0) { if (it.data().useAsFilter) { TQStringList filters = m_part->mimeFilter(); filters.remove(it.key()); m_part->setMimeFilter(filters); SessionManager::self()->save(m_part->url(), filters); // Use 1ms delay to avoid possible race conditions TQTimer::singleShot(1, this, TQT_SLOT(slotTimeout())); } MimeInfoIterator del_mit = it; ++it; m_pMimeInfo.remove(del_mit); } else ++it; } for (KFileItemListIterator fi_it(list); fi_it.current (); ++fi_it) { TQString name = fi_it.current()->name(); KMimeType::Ptr mime = fi_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 { MimeInfo &mimeInfo = m_pMimeInfo[mimeType]; if (!mimeInfo.filenames.contains(name)) { mimeInfo.filenames.insert(name, false); } } } } 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(m_part->scrollWidget()) ) { static_cast(m_searchWidget)->updateSearch(); } else if ( ::tqqt_cast(m_part->scrollWidget()) ) { static_cast(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 DirFilterFactory; K_EXPORT_COMPONENT_FACTORY (libdirfilterplugin, DirFilterFactory("dirfilterplugin")) #include "dirfilterplugin.moc"