/* Gwenview - A simple image viewer for TDE Copyright 2000-2004 Aur�ien G�eau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // TQt #include #include #include #include #include #include #include #include // KDE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Local #include "archive.h" #include "cache.h" #include "clicklineedit.h" #include "cursortracker.h" #include "filedetailview.h" #include "fileoperation.h" #include "filethumbnailview.h" #include "filterbar.h" #include "imageloader.h" #include "mimetypeutils.h" #include "timeutils.h" #include "thumbnailsize.h" #include "fileviewconfig.h" #include "miscconfig.h" #include "fileviewcontroller.moc" namespace Gwenview { #undef ENABLE_LOG #undef LOG //#define ENABLE_LOG #ifdef ENABLE_LOG #define LOG(x) kdDebug() << k_funcinfo << x << endl #else #define LOG(x) ; #endif static const int SLIDER_RESOLUTION=4; //----------------------------------------------------------------------- // // internal class which allows dynamically turning off visual error reporting // //----------------------------------------------------------------------- class DirLister : public KDirLister { public: DirLister() : KDirLister() , mError(false) , mCheck(false) {} virtual bool validURL(const KURL& url) const { if( !url.isValid()) mError = true; if( mCheck ) return KDirLister::validURL( url ); return url.isValid(); } virtual void handleError(TDEIO::Job* job) { mError = true; if(mCheck) KDirLister::handleError( job ); }; bool error() const { return mError; } void clearError() { mError = false; } void setCheck(bool c) { mCheck = c; } void setDateFilter(const TQDate& from, const TQDate& to) { mFromDate = from; mToDate =to; } virtual bool itemMatchFilters(const KFileItem* item) const { if (!matchesFilter(item)) return false; return matchesMimeFilter(item); } public: virtual bool matchesMimeFilter(const KFileItem* item) const { // Do mime filtering ourself because we use startsWith instead of == TQStringList lst = mimeFilters(); TQStringList::Iterator it = lst.begin(), end = lst.end(); bool result = false; TQString type = item->mimetype(); for (; it!=end; ++it) { if (type.startsWith(*it)) { result = true; break; } } if (!result) return false; if (item->isDir() || Archive::fileItemIsArchive(item)) { // Do not filter out dirs or archives return true; } if (!mFromDate.isValid() && !mToDate.isValid()) return result; // Convert item time to a TQDate time_t time=TimeUtils::getTime(item); TQDateTime dateTime; dateTime.setTime_t(time); TQDate date=dateTime.date(); if (mFromDate.isValid() && date < mFromDate) return false; if (mToDate.isValid() && date > mToDate) return false; return true; } private: mutable bool mError; bool mCheck; TQDate mFromDate; TQDate mToDate; }; //----------------------------------------------------------------------- // // FileViewController::Private // //----------------------------------------------------------------------- struct FileViewController::Private { ~Private() { delete mSliderTracker; } FileViewController* that; FilterBar* mFilterBar; TDEToolBar* mToolBar; TQWidgetStack* mStack; TDESelectAction* mSortAction; TDEToggleAction* mRevertSortAction; TipTracker* mSliderTracker; TQHBox* mFilterHBox; TQComboBox* mFilterComboBox; TQCheckBox* mShowFilterBarCheckBox; TDEIO::MetaInfoJob* mMetaInfoJob; KFileItemList mMetaInfoTodo; void initFilterBar() { mFilterBar=new FilterBar(that); mFilterBar->setSizePolicy(TQSizePolicy::Preferred, TQSizePolicy::Fixed); mFilterBar->hide(); TQIconSet resetIS=BarIcon("locationbar_erase"); mFilterBar->mResetNameCombo->setIconSet(resetIS); mFilterBar->mResetFrom->setIconSet(resetIS); mFilterBar->mResetTo->setIconSet(resetIS); TQObject::connect( mFilterBar->mResetNameCombo, TQ_SIGNAL(clicked()), that, TQ_SLOT(resetNameFilter()) ); TQObject::connect( mFilterBar->mResetFrom, TQ_SIGNAL(clicked()), that, TQ_SLOT(resetFromFilter()) ); TQObject::connect( mFilterBar->mResetTo, TQ_SIGNAL(clicked()), that, TQ_SLOT(resetToFilter()) ); TQObject::connect( mFilterBar->mFilterButton, TQ_SIGNAL(clicked()), that, TQ_SLOT(applyFilter()) ); } void initFilterCombo() { mFilterHBox=new TQHBox(mToolBar, "tde toolbar widget"); mFilterHBox->setSpacing(KDialog::spacingHint()); mFilterComboBox=new TQComboBox(mFilterHBox); mFilterComboBox->insertItem(i18n("All files"), ALL); mFilterComboBox->insertItem(i18n("Images only"), IMAGES_ONLY); mFilterComboBox->insertItem(i18n("Videos only"), VIDEOS_ONLY); TQObject::connect( mFilterComboBox, TQ_SIGNAL(activated(int)), that, TQ_SLOT(applyFilter()) ); mShowFilterBarCheckBox = new TQCheckBox(i18n("More"), mFilterHBox); TQObject::connect( mShowFilterBarCheckBox, TQ_SIGNAL(toggled(bool)), mFilterBar, TQ_SLOT(setShown(bool)) ); TQObject::connect( mShowFilterBarCheckBox, TQ_SIGNAL(toggled(bool)), that, TQ_SLOT(applyFilter()) ); } void loadFilterSettings() { mFilterComboBox->setCurrentItem(FileViewConfig::filterMode()); mShowFilterBarCheckBox->setChecked(FileViewConfig::showFilterBar()); mFilterBar->mNameEdit->setText(FileViewConfig::nameFilter()); mFilterBar->mFromDateEdit->setDate(FileViewConfig::fromDateFilter().date()); mFilterBar->mToDateEdit->setDate(FileViewConfig::toDateFilter().date()); } }; //----------------------------------------------------------------------- // // FileViewController // //----------------------------------------------------------------------- FileViewController::FileViewController(TQWidget* parent,TDEActionCollection* actionCollection) : TQWidget(parent) , mMode(FILE_LIST) , mPrefetch( NULL ) , mChangeDirStatus(CHANGE_DIR_STATUS_NONE) , mBrowsing(false) , mSelecting(false) { d=new Private; d->that=this; setMinimumWidth(1); d->mToolBar=new TDEToolBar(this, "", true); d->initFilterBar(); d->initFilterCombo(); d->mStack=new TQWidgetStack(this); d->mMetaInfoJob=NULL; TQVBoxLayout *layout=new TQVBoxLayout(this); layout->addWidget(d->mToolBar); layout->addWidget(d->mFilterBar); layout->addWidget(d->mStack); // Actions mSelectFirst=new TDEAction(i18n("&First"), TQApplication::reverseLayout() ? "2rightarrow":"2leftarrow", Key_Home, this,TQ_SLOT(slotSelectFirst()), actionCollection, "first"); mSelectLast=new TDEAction(i18n("&Last"), TQApplication::reverseLayout() ? "2leftarrow":"2rightarrow", Key_End, this,TQ_SLOT(slotSelectLast()), actionCollection, "last"); mSelectPrevious=new TDEAction(i18n("&Previous"), TQApplication::reverseLayout() ? "1rightarrow":"1leftarrow", Key_BackSpace, this,TQ_SLOT(slotSelectPrevious()), actionCollection, "previous"); mSelectNext=new TDEAction(i18n("&Next"), TQApplication::reverseLayout() ? "1leftarrow":"1rightarrow", Key_Space, this,TQ_SLOT(slotSelectNext()), actionCollection, "next"); mSelectPreviousDir=new TDEAction(i18n("&Previous Folder"), TQApplication::reverseLayout() ? "media-seek-forward":"media-seek-backward", ALT + Key_BackSpace, this,TQ_SLOT(slotSelectPreviousDir()), actionCollection, "previous_folder"); mSelectNextDir=new TDEAction(i18n("&Next Folder"), TQApplication::reverseLayout() ? "media-seek-backward":"media-seek-forward", ALT + Key_Space, this,TQ_SLOT(slotSelectNextDir()), actionCollection, "next_folder"); mSelectFirstSubDir=new TDEAction(i18n("&First Sub Folder"), "go-down", ALT + Key_Down, this,TQ_SLOT(slotSelectFirstSubDir()), actionCollection, "first_sub_folder"); mListMode=new TDERadioAction(i18n("Details"),"view_detailed",0,this,TQ_SLOT(updateViewMode()),actionCollection,"list_mode"); mListMode->setExclusiveGroup("thumbnails"); mSideThumbnailMode=new TDERadioAction(i18n("Thumbnails with Info on Side"),"view_multicolumn",0,this,TQ_SLOT(updateViewMode()),actionCollection,"side_thumbnail_mode"); mSideThumbnailMode->setExclusiveGroup("thumbnails"); mBottomThumbnailMode=new TDERadioAction(i18n("Thumbnails with Info on Bottom"),"view_icon",0,this,TQ_SLOT(updateViewMode()),actionCollection,"bottom_thumbnail_mode"); mBottomThumbnailMode->setExclusiveGroup("thumbnails"); // Size slider mSizeSlider=new TQSlider(Horizontal, d->mToolBar); mSizeSlider->setFixedWidth(120); mSizeSlider->setRange( ThumbnailSize::MIN/SLIDER_RESOLUTION, ThumbnailSize::LARGE/SLIDER_RESOLUTION); mSizeSlider->setValue(FileViewConfig::thumbnailSize() / SLIDER_RESOLUTION); connect(mSizeSlider, TQ_SIGNAL(valueChanged(int)), this, TQ_SLOT(updateThumbnailSize(int)) ); connect(mListMode, TQ_SIGNAL(toggled(bool)), mSizeSlider, TQ_SLOT(setDisabled(bool)) ); TDEAction* sliderAction=new KWidgetAction(mSizeSlider, i18n("Thumbnail Size"), 0, 0, 0, actionCollection, "size_slider"); d->mSliderTracker=new TipTracker("", mSizeSlider); // /Size slider mShowDotFiles=new TDEToggleAction(i18n("Show &Hidden Files"),CTRL + Key_H,this,TQ_SLOT(toggleShowDotFiles()),actionCollection,"show_dot_files"); d->mSortAction=new TDESelectAction(i18n("Sort"), 0, this, TQ_SLOT(setSorting()), actionCollection, "view_sort"); TQStringList sortItems; sortItems << i18n("By Name") << i18n("By Date") << i18n("By Size"); d->mSortAction->setItems(sortItems); d->mSortAction->setCurrentItem(0); d->mRevertSortAction=new TDEToggleAction(i18n("Descending"),0, this, TQ_SLOT(setSorting()), actionCollection, "descending"); TQPopupMenu* sortMenu=d->mSortAction->popupMenu(); Q_ASSERT(sortMenu); sortMenu->insertSeparator(); d->mRevertSortAction->plug(sortMenu); // Dir lister mDirLister=new DirLister; mDirLister->setMainWindow(topLevelWidget()); connect(mDirLister,TQ_SIGNAL(clear()), this,TQ_SLOT(dirListerClear()) ); connect(mDirLister,TQ_SIGNAL(newItems(const KFileItemList&)), this,TQ_SLOT(dirListerNewItems(const KFileItemList&)) ); connect(mDirLister,TQ_SIGNAL(deleteItem(KFileItem*)), this,TQ_SLOT(dirListerDeleteItem(KFileItem*)) ); connect(mDirLister,TQ_SIGNAL(refreshItems(const KFileItemList&)), this,TQ_SLOT(dirListerRefreshItems(const KFileItemList&)) ); connect(mDirLister,TQ_SIGNAL(started(const KURL&)), this,TQ_SLOT(dirListerStarted()) ); connect(mDirLister,TQ_SIGNAL(completed()), this,TQ_SLOT(dirListerCompleted()) ); connect(mDirLister,TQ_SIGNAL(canceled()), this,TQ_SLOT(dirListerCanceled()) ); // Propagate canceled signals connect(mDirLister,TQ_SIGNAL(canceled()), this,TQ_SIGNAL(canceled()) ); // File detail widget mFileDetailView=new FileDetailView(d->mStack, "filedetailview"); d->mStack->addWidget(mFileDetailView,0); mFileDetailView->viewport()->installEventFilter(this); connect(mFileDetailView,TQ_SIGNAL(executed(TQListViewItem*)), this,TQ_SLOT(slotViewExecuted()) ); connect(mFileDetailView,TQ_SIGNAL(returnPressed(TQListViewItem*)), this,TQ_SLOT(slotViewExecuted()) ); connect(mFileDetailView,TQ_SIGNAL(currentChanged(TQListViewItem*)), this,TQ_SLOT(slotViewClicked()) ); connect(mFileDetailView,TQ_SIGNAL(selectionChanged()), this,TQ_SLOT(slotViewClicked()) ); connect(mFileDetailView,TQ_SIGNAL(clicked(TQListViewItem*)), this,TQ_SLOT(slotViewClicked()) ); connect(mFileDetailView,TQ_SIGNAL(contextMenu(TDEListView*, TQListViewItem*, const TQPoint&)), this,TQ_SLOT(openContextMenu(TDEListView*, TQListViewItem*, const TQPoint&)) ); connect(mFileDetailView,TQ_SIGNAL(dropped(TQDropEvent*,KFileItem*)), this,TQ_SLOT(openDropURLMenu(TQDropEvent*, KFileItem*)) ); connect(mFileDetailView, TQ_SIGNAL(sortingChanged(TQDir::SortSpec)), this, TQ_SLOT(updateSortMenu(TQDir::SortSpec)) ); connect(mFileDetailView, TQ_SIGNAL(doubleClicked(TQListViewItem*)), this, TQ_SLOT(slotViewDoubleClicked()) ); connect(mFileDetailView, TQ_SIGNAL(selectionChanged()), this, TQ_SIGNAL(selectionChanged()) ); // Thumbnail widget mFileThumbnailView=new FileThumbnailView(d->mStack); d->mStack->addWidget(mFileThumbnailView,1); mFileThumbnailView->viewport()->installEventFilter(this); connect(mFileThumbnailView,TQ_SIGNAL(executed(TQIconViewItem*)), this,TQ_SLOT(slotViewExecuted()) ); connect(mFileThumbnailView,TQ_SIGNAL(returnPressed(TQIconViewItem*)), this,TQ_SLOT(slotViewExecuted()) ); connect(mFileThumbnailView,TQ_SIGNAL(currentChanged(TQIconViewItem*)), this,TQ_SLOT(slotViewClicked()) ); connect(mFileThumbnailView,TQ_SIGNAL(selectionChanged()), this,TQ_SLOT(slotViewClicked()) ); connect(mFileThumbnailView,TQ_SIGNAL(clicked(TQIconViewItem*)), this,TQ_SLOT(slotViewClicked()) ); connect(mFileThumbnailView,TQ_SIGNAL(contextMenuRequested(TQIconViewItem*,const TQPoint&)), this,TQ_SLOT(openContextMenu(TQIconViewItem*,const TQPoint&)) ); connect(mFileThumbnailView,TQ_SIGNAL(dropped(TQDropEvent*,KFileItem*)), this,TQ_SLOT(openDropURLMenu(TQDropEvent*, KFileItem*)) ); connect(mFileThumbnailView, TQ_SIGNAL(doubleClicked(TQIconViewItem*)), this, TQ_SLOT(slotViewDoubleClicked()) ); connect(mFileThumbnailView, TQ_SIGNAL(selectionChanged()), this, TQ_SIGNAL(selectionChanged()) ); // Thumbnail details dialog TDEAction* thumbnailDetailsDialogAction=new TDEAction(i18n("Edit Thumbnail Details..."), "configure", 0, mFileThumbnailView, TQ_SLOT(showThumbnailDetailsDialog()), actionCollection, "thumbnail_details_dialog"); connect(mBottomThumbnailMode, TQ_SIGNAL(toggled(bool)), thumbnailDetailsDialogAction, TQ_SLOT(setEnabled(bool)) ); // Fill toolbar mListMode->plug(d->mToolBar); mSideThumbnailMode->plug(d->mToolBar); mBottomThumbnailMode->plug(d->mToolBar); d->mToolBar->insertSeparator(); sliderAction->plug(d->mToolBar); d->mToolBar->insertSeparator(); thumbnailDetailsDialogAction->plug(d->mToolBar); int id=d->mToolBar->insertWidget(-1, 0, d->mFilterHBox); d->mToolBar->alignItemRight(id, true); mShowDotFiles->setChecked(FileViewConfig::showDotFiles()); bool startWithThumbnails=FileViewConfig::startWithThumbnails(); setMode(startWithThumbnails?THUMBNAIL:FILE_LIST); mSizeSlider->setEnabled(startWithThumbnails); if (startWithThumbnails) { if (mFileThumbnailView->itemTextPos()==TQIconView::Right) { mSideThumbnailMode->setChecked(true); } else { mBottomThumbnailMode->setChecked(true); } // Make sure the thumbnail view and the slider tooltip are updated updateThumbnailSize(mSizeSlider->value()); mFileThumbnailView->startThumbnailUpdate(); } else { mListMode->setChecked(true); } thumbnailDetailsDialogAction->setEnabled(mBottomThumbnailMode->isChecked()); if (MiscConfig::rememberFilter()) { d->loadFilterSettings(); } updateFromSettings(); } FileViewController::~FileViewController() { // Save various settings FileViewConfig::setStartWithThumbnails(mMode==THUMBNAIL); int filterMode = d->mFilterComboBox->currentItem(); FileViewConfig::setFilterMode(filterMode); FileViewConfig::setShowFilterBar(d->mShowFilterBarCheckBox->isChecked()); FileViewConfig::setNameFilter(d->mFilterBar->mNameEdit->text()); FileViewConfig::setFromDateFilter(d->mFilterBar->mFromDateEdit->date()); FileViewConfig::setToDateFilter(d->mFilterBar->mToDateEdit->date()); FileViewConfig::writeConfig(); stopMetaInfoUpdate(); delete mDirLister; delete d; } void FileViewController::setFocus() { currentFileView()->widget()->setFocus(); } /** * Do not let double click events propagate if Ctrl or Shift is down, to avoid * toggling fullscreen */ bool FileViewController::eventFilter(TQObject*, TQEvent* event) { if (event->type()!=TQEvent::MouseButtonDblClick) return false; TQMouseEvent* mouseEvent=static_cast(event); if (mouseEvent->state() & TQt::ControlButton || mouseEvent->state() & TQt::ShiftButton) { return true; } return false; } bool FileViewController::lastURLError() const { return mDirLister->error(); } //----------------------------------------------------------------------- // // Public slots // //----------------------------------------------------------------------- void FileViewController::setDirURL(const KURL& url) { LOG(url.prettyURL()); if ( mDirURL.equals(url,true) ) { LOG("Same URL"); return; } prefetchDone(); mDirURL=url; if (!KProtocolInfo::supportsListing(mDirURL)) { LOG("Protocol does not support listing"); return; } mDirLister->clearError(); currentFileView()->setShownFileItem(0L); mFileNameToSelect=TQString(); mDirLister->openURL(mDirURL); emit urlChanged(mDirURL); emit directoryChanged(mDirURL); updateActions(); } /** * Sets the file to select once the dir lister is done. If it's not running, * immediatly selects the file. */ void FileViewController::setFileNameToSelect(const TQString& fileName) { mFileNameToSelect=fileName; if (mDirLister->isFinished()) { browseToFileNameToSelect(); } } void FileViewController::prefetch( KFileItem* item ) { prefetchDone(); if( item == NULL ) return; mPrefetch = ImageLoader::loader( item->url(), this, BUSY_PRELOADING ); connect( mPrefetch, TQ_SIGNAL( imageLoaded( bool )), TQ_SLOT( prefetchDone())); } void FileViewController::prefetchDone() { if( mPrefetch != NULL ) { mPrefetch->release( this ); mPrefetch = NULL; } } void FileViewController::slotSelectFirst() { browseTo( findFirstImage()); prefetch( findNextImage()); } void FileViewController::slotSelectLast() { browseTo(findLastImage()); prefetch( findPreviousImage()); } void FileViewController::slotSelectPrevious() { browseTo(findPreviousImage()); prefetch( findPreviousImage()); } void FileViewController::slotSelectNext() { browseTo(findNextImage()); prefetch( findNextImage()); } void FileViewController::slotSelectPreviousDir() { mChangeDirStatus = CHANGE_DIR_STATUS_PREV; mDirLister->clearError(); mDirLister->openURL(mDirURL.upURL()); } void FileViewController::slotSelectNextDir() { mChangeDirStatus = CHANGE_DIR_STATUS_NEXT; mDirLister->clearError(); mDirLister->openURL(mDirURL.upURL()); } void FileViewController::slotSelectFirstSubDir() { KFileItem* item=currentFileView()->firstFileItem(); while (item && !Archive::fileItemIsDirOrArchive(item)) { item=currentFileView()->nextItem(item); } if (!item) { LOG("No item found"); return; } LOG("item->url(): " << item->url().prettyURL()); KURL tmp=item->url(); if (Archive::fileItemIsArchive(item)) { tmp.setProtocol(Archive::protocolForMimeType(item->mimetype())); } tmp.adjustPath(1); setDirURL(tmp); } void FileViewController::resetNameFilter() { d->mFilterBar->mNameEdit->clear(); } void FileViewController::resetFromFilter() { d->mFilterBar->mFromDateEdit->setDate(TQDate()); } void FileViewController::resetToFilter() { d->mFilterBar->mToDateEdit->setDate(TQDate()); } void FileViewController::browseTo(KFileItem* item) { prefetchDone(); if (mBrowsing) return; mBrowsing = true; if (item) { currentFileView()->setCurrentItem(item); currentFileView()->clearSelection(); currentFileView()->setSelected(item,true); currentFileView()->ensureItemVisible(item); if (!item->isDir() && !Archive::fileItemIsArchive(item)) { emitURLChanged(); } } updateActions(); mBrowsing = false; } void FileViewController::browseToFileNameToSelect() { // There's something to select if (!mFileNameToSelect.isEmpty()) { browseTo(findItemByFileName(mFileNameToSelect)); mFileNameToSelect=TQString(); return; } // Nothing to select, but an item is already shown if (currentFileView()->shownFileItem()) return; // Now we have to make some default choice slotSelectFirst(); // If no item is selected, make sure the first one is if (currentFileView()->selectedItems()->count()==0) { KFileItem* item=currentFileView()->firstFileItem(); if (item) { currentFileView()->setCurrentItem(item); currentFileView()->setSelected(item, true); currentFileView()->ensureItemVisible(item); } } } void FileViewController::updateThumbnail(const KURL& url) { if (mMode==FILE_LIST) return; KFileItem* item=mDirLister->findByURL(url); if (!item) return; mFileThumbnailView->updateThumbnail(item); } //----------------------------------------------------------------------- // // Private slots // //----------------------------------------------------------------------- void FileViewController::slotViewExecuted() { KFileItem* item=currentFileView()->currentFileItem(); if (!item) return; bool isDir=item->isDir(); bool isArchive=Archive::fileItemIsArchive(item); if (isDir || isArchive) { KURL tmp=url(); if (isArchive) { tmp.setProtocol(Archive::protocolForMimeType(item->mimetype())); } tmp.adjustPath(1); setDirURL(tmp); } else { emitURLChanged(); } } void FileViewController::slotViewClicked() { updateActions(); KFileItem* item=currentFileView()->currentFileItem(); if (!item || Archive::fileItemIsDirOrArchive(item)) return; mSelecting = true; emitURLChanged(); mSelecting = false; } void FileViewController::slotViewDoubleClicked() { updateActions(); KFileItem* item=currentFileView()->currentFileItem(); if (item && !Archive::fileItemIsDirOrArchive(item)) emit imageDoubleClicked(); } void FileViewController::updateViewMode() { KFileItem* currentItem=currentFileView()->currentFileItem(); if (mListMode->isChecked()) { setMode(FILE_LIST); return; } if (mSideThumbnailMode->isChecked()) { mFileThumbnailView->setItemTextPos(TQIconView::Right); } else { mFileThumbnailView->setItemTextPos(TQIconView::Bottom); } // Only switch the view if we are going from no thumbs to either side or // bottom thumbs, not when switching between side and bottom thumbs if (mMode==FILE_LIST) { setMode(THUMBNAIL); } else { KFileItemList items=*mFileThumbnailView->items(); KFileItem* shownFileItem=mFileThumbnailView->shownFileItem(); mFileThumbnailView->FileViewBase::clear(); mFileThumbnailView->addItemList(items); mFileThumbnailView->setShownFileItem(shownFileItem); } browseTo(currentItem); updateThumbnailSize(mSizeSlider->value()); mFileThumbnailView->startThumbnailUpdate(); } void FileViewController::updateThumbnailSize(int size) { size*=SLIDER_RESOLUTION; d->mSliderTracker->setText(i18n("Thumbnail size: %1x%2").arg(size).arg(size)); FileViewConfig::setThumbnailSize(size); mFileThumbnailView->setThumbnailSize(size); Cache::instance()->checkThumbnailSize(size); } void FileViewController::toggleShowDotFiles() { mDirLister->setShowingDotFiles(mShowDotFiles->isChecked()); mDirLister->openURL(mDirURL); } void FileViewController::updateSortMenu(TQDir::SortSpec _spec) { int spec=_spec & (TQDir::Name | TQDir::Time | TQDir::Size); int item; switch (spec) { case TQDir::Name: item=0; break; case TQDir::Time: item=1; break; case TQDir::Size: item=2; break; default: item=-1; break; } d->mSortAction->setCurrentItem(item); } void FileViewController::setSorting() { TQDir::SortSpec spec; switch (d->mSortAction->currentItem()) { case 0: // Name spec=TQDir::Name; break; case 1: // Date spec=TQDir::Time; break; case 2: // Size spec=TQDir::Size; break; default: return; } if (d->mRevertSortAction->isChecked()) { spec=TQDir::SortSpec(spec | TQDir::Reversed); } currentFileView()->setSorting(TQDir::SortSpec(spec | TQDir::DirsFirst)); emit sortingChanged(); } //----------------------------------------------------------------------- // // Context menu // //----------------------------------------------------------------------- void FileViewController::openContextMenu(TDEListView*,TQListViewItem* item,const TQPoint& pos) { emit requestContextMenu(pos, item!=0); } void FileViewController::openContextMenu(TQIconViewItem* item,const TQPoint& pos) { emit requestContextMenu(pos, item!=0); } //----------------------------------------------------------------------- // // Drop URL menu // //----------------------------------------------------------------------- void FileViewController::openDropURLMenu(TQDropEvent* event, KFileItem* item) { KURL dest; if (item) { dest=item->url(); } else { dest=mDirURL; } KURL::List urls; if (!KURLDrag::decode(event,urls)) return; FileOperation::openDropURLMenu(d->mStack, urls, dest); } //----------------------------------------------------------------------- // // File operations // //----------------------------------------------------------------------- KURL::List FileViewController::selectedURLs() const { KURL::List list; KFileItemListIterator it( *currentFileView()->selectedItems() ); for ( ; it.current(); ++it ) { list.append(it.current()->url()); } if (list.isEmpty()) { const KFileItem* item=currentFileView()->shownFileItem(); if (item) list.append(item->url()); } return list; } KURL::List FileViewController::selectedImageURLs() const { KURL::List list; KFileItemListIterator it( *currentFileView()->selectedItems() ); for ( ; it.current(); ++it ) { KFileItem* item=it.current(); if (!Archive::fileItemIsDirOrArchive(item)) { list.append(item->url()); } } if (list.isEmpty()) { const KFileItem* item=currentFileView()->shownFileItem(); if (item && !Archive::fileItemIsDirOrArchive(item)) list.append(item->url()); } return list; } //----------------------------------------------------------------------- // // Properties // //----------------------------------------------------------------------- TQString FileViewController::fileName() const { KFileItem* item=currentFileView()->currentFileItem(); if (!item) return ""; return item->text(); } FileViewBase* FileViewController::currentFileView() const { if (mMode==FILE_LIST) { return mFileDetailView; } else { return mFileThumbnailView; } } uint FileViewController::fileCount() const { uint count=currentFileView()->count(); KFileItem* item=currentFileView()->firstFileItem(); while (item && Archive::fileItemIsDirOrArchive(item)) { item=currentFileView()->nextItem(item); count--; } return count; } int FileViewController::shownFilePosition() const { KFileItem* shownItem=currentFileView()->shownFileItem(); if (!shownItem) return -1; KFileItem* item=currentFileView()->firstFileItem(); int position=0; for (; item && item!=shownItem; item=currentFileView()->nextItem(item) ) { if (!Archive::fileItemIsDirOrArchive(item)) ++position; } return position; } KURL FileViewController::url() const { KFileItem* item=currentFileView()->currentFileItem(); if (!item) return mDirURL; return item->url(); } KURL FileViewController::dirURL() const { return mDirURL; } uint FileViewController::selectionSize() const { const KFileItemList* selectedItems=currentFileView()->selectedItems(); return selectedItems->count(); } void FileViewController::setMode(FileViewController::Mode mode) { const KFileItemList* items; FileViewBase* oldView; FileViewBase* newView; mMode=mode; if (mMode==FILE_LIST) { mFileThumbnailView->stopThumbnailUpdate(); oldView=mFileThumbnailView; newView=mFileDetailView; } else { oldView=mFileDetailView; newView=mFileThumbnailView; } bool wasFocused=oldView->widget()->hasFocus(); // Show the new active view d->mStack->raiseWidget(newView->widget()); if (wasFocused) newView->widget()->setFocus(); // Fill the new view newView->clear(); newView->addItemList(*oldView->items()); // Set the new view to the same state as the old items=oldView->selectedItems(); for(KFileItemListIterator it(*items);it.current()!=0L;++it) { newView->setSelected(it.current(), true); } newView->setShownFileItem(oldView->shownFileItem()); newView->setCurrentItem(oldView->currentFileItem()); // Remove references to the old view from KFileItems items=oldView->items(); for(KFileItemListIterator it(*items);it.current()!=0L;++it) { it.current()->removeExtraData(oldView); } // Update sorting newView->setSorting(oldView->sorting()); // Clear the old view oldView->FileViewBase::clear(); } void FileViewController::updateFromSettings() { applyFilter(); mFileThumbnailView->setMarginSize(FileViewConfig::thumbnailMarginSize()); mFileThumbnailView->setItemDetails(FileViewConfig::thumbnailDetails()); currentFileView()->widget()->update(); } void FileViewController::setSilentMode( bool silent ) { mDirLister->setCheck( !silent ); } void FileViewController::retryURL() { mDirLister->clearError(); mDirLister->openURL(url().directory()); } //----------------------------------------------------------------------- // // Dir lister slots // //----------------------------------------------------------------------- void FileViewController::dirListerDeleteItem(KFileItem* item) { KFileItem* newShownItem=0L; const KFileItem* shownItem=currentFileView()->shownFileItem(); if (shownItem==item) { newShownItem=findNextImage(); if (!newShownItem) newShownItem=findPreviousImage(); } // Remove item from metadata todo list and job's pending update lists d->mMetaInfoTodo.remove(item); if (d->mMetaInfoJob) { d->mMetaInfoJob->removeItem(item); } currentFileView()->removeItem(item); if (shownItem==item) { currentFileView()->setCurrentItem(newShownItem); currentFileView()->setSelected(newShownItem, true); if (newShownItem) { emit urlChanged(newShownItem->url()); } else { emit urlChanged(KURL()); } } } void FileViewController::dirListerNewItems(const KFileItemList& items) { LOG(""); mThumbnailsNeedUpdate=true; currentFileView()->addItemList(items); loadMetaInfo(items); } void FileViewController::dirListerRefreshItems(const KFileItemList& list) { LOG(""); KFileItemListIterator it(list); for (; *it!=0L; ++it) { updateViewItem(*it); } loadMetaInfo(list, true); } void FileViewController::updateViewItem(const KFileItem *item, bool metaDataOnly) { currentFileView()->updateView(item, metaDataOnly); if (item==currentFileView()->shownFileItem()) { emit shownFileItemRefreshed(item); } } void FileViewController::refreshItems(const KURL::List& urls) { LOG(""); KFileItemList list; for( KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it ) { KURL dir = *it; dir.setFileName( TQString() ); if( dir != mDirURL ) continue; // TODO this could be quite slow for many images? KFileItem* item = findItemByFileName( (*it).filename()); if( item ) list.append( item ); } dirListerRefreshItems( list ); } void FileViewController::dirListerClear() { currentFileView()->clear(); } void FileViewController::dirListerStarted() { LOG(""); mThumbnailsNeedUpdate=false; } void FileViewController::dirListerCompleted() { LOG(""); // Delay the code to be executed when the dir lister has completed its job // to avoid crash in KDirLister (see bug #57991) TQTimer::singleShot(0,this,TQ_SLOT(delayedDirListerCompleted())); } void FileViewController::delayedDirListerCompleted() { // The call to sort() is a work around to a bug which causes // FileThumbnailView::firstFileItem() to return a wrong item. This work // around is not in firstFileItem() because it's const and sort() is a non // const method if (mMode!=FILE_LIST) { mFileThumbnailView->sortView(); } if (mChangeDirStatus != CHANGE_DIR_STATUS_NONE) { KFileItem *item; TQString fileName = mDirURL.filename(); for (item=currentFileView()->firstFileItem(); item; item=currentFileView()->nextItem(item) ) { if (item->name() == fileName) { if (mChangeDirStatus == CHANGE_DIR_STATUS_NEXT) { do { item=currentFileView()->nextItem(item); } while (item && !Archive::fileItemIsDirOrArchive(item)); } else { do { item=currentFileView()->prevItem(item); } while (item && !Archive::fileItemIsDirOrArchive(item)); } break; }; } mChangeDirStatus = CHANGE_DIR_STATUS_NONE; if (!item) { mDirLister->openURL(mDirURL); } else { KURL tmp=item->url(); LOG("item->url(): " << item->url().prettyURL()); if (Archive::fileItemIsArchive(item)) { tmp.setProtocol(Archive::protocolForMimeType(item->mimetype())); } tmp.adjustPath(1); setDirURL(tmp); } } else { browseToFileNameToSelect(); emit completed(); if (mMode!=FILE_LIST && mThumbnailsNeedUpdate) { mFileThumbnailView->startThumbnailUpdate(); } } } void FileViewController::dirListerCanceled() { if (mMode!=FILE_LIST) { mFileThumbnailView->stopThumbnailUpdate(); } stopMetaInfoUpdate(); browseToFileNameToSelect(); } void FileViewController::setShowFilterBar(bool value) { d->mShowFilterBarCheckBox->setChecked(value); } void FileViewController::setFilterMode(int mode) { d->mFilterComboBox->setCurrentItem(mode); } void FileViewController::setFilterName(const TQString& name) { d->mFilterBar->mNameEdit->setText(name); } void FileViewController::setFilterFromDate(const TQDate& date) { d->mFilterBar->mFromDateEdit->setDate(date); } void FileViewController::setFilterToDate(const TQDate& date) { d->mFilterBar->mToDateEdit->setDate(date); } void FileViewController::applyFilter() { TQStringList mimeTypes; FilterMode filterMode = static_cast( d->mFilterComboBox->currentItem() ); if (FileViewConfig::showDirs()) { mimeTypes << "inode/directory"; mimeTypes += Archive::mimeTypes(); } if (filterMode != VIDEOS_ONLY) { mimeTypes += MimeTypeUtils::rasterImageMimeTypes(); mimeTypes << "image/svg"; } if (filterMode != IMAGES_ONLY) { mimeTypes << "video/"; } if (d->mShowFilterBarCheckBox->isChecked()) { TQString txt=d->mFilterBar->mNameEdit->text(); TQDate from=d->mFilterBar->mFromDateEdit->date(); TQDate to=d->mFilterBar->mToDateEdit->date(); mDirLister->setNameFilter(txt); mDirLister->setDateFilter(from, to); } else { mDirLister->setNameFilter(TQString()); mDirLister->setDateFilter(TQDate(), TQDate()); } mDirLister->setShowingDotFiles(mShowDotFiles->isChecked()); mDirLister->setMimeFilter(mimeTypes); // Find next item matching the filter if any, so that we can keep it // current KFileItem* item=currentFileView()->currentFileItem(); for (; item; item=currentFileView()->nextItem(item)) { if (mDirLister->itemMatchFilters(item)) { mFileNameToSelect=item->name(); break; } } mDirLister->openURL(mDirURL); } void FileViewController::updateActions() { KFileItem* firstImage=findFirstImage(); // There isn't any image, no need to continue if (!firstImage) { mSelectFirst->setEnabled(false); mSelectPrevious->setEnabled(false); mSelectNext->setEnabled(false); mSelectLast->setEnabled(false); return; } // We did not select any image, let's activate everything KFileItem* currentItem=currentFileView()->currentFileItem(); if (!currentItem || Archive::fileItemIsDirOrArchive(currentItem)) { mSelectFirst->setEnabled(true); mSelectPrevious->setEnabled(true); mSelectNext->setEnabled(true); mSelectLast->setEnabled(true); return; } // There is at least one image, and an image is selected, let's be precise bool isFirst=currentItem==firstImage; bool isLast=currentItem==findLastImage(); mSelectFirst->setEnabled(!isFirst); mSelectPrevious->setEnabled(!isFirst); mSelectNext->setEnabled(!isLast); mSelectLast->setEnabled(!isLast); } void FileViewController::emitURLChanged() { KFileItem* item=currentFileView()->currentFileItem(); currentFileView()->setShownFileItem(item); // We use a tmp value because the signal parameter is a reference KURL tmp=url(); LOG("urlChanged: " << tmp.prettyURL()); emit urlChanged(tmp); } KFileItem* FileViewController::findFirstImage() const { KFileItem* item=currentFileView()->firstFileItem(); while (item && Archive::fileItemIsDirOrArchive(item)) { item=currentFileView()->nextItem(item); } if (item) { LOG("item->url(): " << item->url().prettyURL()); } else { LOG("No item found"); } return item; } KFileItem* FileViewController::findLastImage() const { KFileItem* item=currentFileView()->items()->getLast(); while (item && Archive::fileItemIsDirOrArchive(item)) { item=currentFileView()->prevItem(item); } return item; } KFileItem* FileViewController::findPreviousImage() const { KFileItem* item=currentFileView()->shownFileItem(); if (!item) return 0L; do { item=currentFileView()->prevItem(item); } while (item && Archive::fileItemIsDirOrArchive(item)); return item; } KFileItem* FileViewController::findNextImage() const { KFileItem* item=currentFileView()->shownFileItem(); if (!item) return 0L; do { item=currentFileView()->nextItem(item); } while (item && Archive::fileItemIsDirOrArchive(item)); return item; } KFileItem* FileViewController::findItemByFileName(const TQString& fileName) const { KFileItem *item; if (fileName.isEmpty()) return 0L; for (item=currentFileView()->firstFileItem(); item; item=currentFileView()->nextItem(item) ) { if (item->name()==fileName) return item; } return 0L; } //----------------------------------------------------------------------------- // // MetaInfo loading // //----------------------------------------------------------------------------- void FileViewController::loadMetaInfo(const KFileItemList& items, bool forceReload) { if (forceReload) { // Reset metadata of all items if we want MetaInfoJob to reload it for(const auto& item: items) { item->setMetaInfo(KFileMetaInfo()); } } if(d->mMetaInfoTodo.isEmpty()) { d->mMetaInfoTodo = items; } else { for(const auto& item: items) { d->mMetaInfoTodo.append(item); } } if(!d->mMetaInfoJob) { nextMetaInfoJob(); } } void FileViewController::nextMetaInfoJob() { if( !d->mMetaInfoTodo.isEmpty() ) { d->mMetaInfoJob = new TDEIO::MetaInfoJob(d->mMetaInfoTodo); connect( d->mMetaInfoJob, TQ_SIGNAL(result(TDEIO::Job *)), this, TQ_SLOT(nextMetaInfoJob()) ); connect( d->mMetaInfoJob, TQ_SIGNAL( gotMetaInfo(const KFileItem *)), this, TQ_SLOT(updateItemMetaInfo(const KFileItem *)) ); d->mMetaInfoTodo.clear(); } else { d->mMetaInfoJob = NULL; // When we finished all metadata load we should update view sorting // We can't do it during the item update due to performance impact if (mMode!=FILE_LIST) { mFileThumbnailView->sortView(); } } } void FileViewController::stopMetaInfoUpdate() { d->mMetaInfoTodo.clear(); if(d->mMetaInfoJob) { d->mMetaInfoJob->kill(); d->mMetaInfoJob = 0; } } void FileViewController::updateItemMetaInfo(const KFileItem *item) { updateViewItem(item, /* metaInfoOnly = */ true); } } // namespace