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.
gwenview/src/gvcore/fileviewcontroller.cpp

1377 lines
38 KiB

/*
Gwenview - A simple image viewer for TDE
Copyright 2000-2004 Aur<75>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 <tqcheckbox.h>
#include <tqdatetimeedit.h>
#include <tqhbox.h>
#include <tqlayout.h>
#include <tqpopupmenu.h>
#include <tqpushbutton.h>
#include <tqtooltip.h>
#include <tqwidgetstack.h>
// KDE
#include <tdeaction.h>
#include <tdeapplication.h>
#include <kcombobox.h>
#include <kdebug.h>
#include <kdirlister.h>
#include <kicontheme.h>
#include <kiconeffect.h>
#include <kiconloader.h>
#include <kimageio.h>
#include <tdelistview.h>
#include <tdelocale.h>
#include <kpropertiesdialog.h>
#include <kprotocolinfo.h>
#include <kstdaction.h>
#include <tdetoolbar.h>
#include <tdetoolbarlabelaction.h>
#include <kurldrag.h>
#include <tdeio/job.h>
#include <tdeio/file.h>
#include <tdeio/metainfojob.h>
// 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
//
//-----------------------------------------------------------------------
class FileViewController::Private {
public:
~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<TQMouseEvent*>(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() {
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);
}
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);
}
}
void FileViewController::updateViewItem(const KFileItem *item) {
currentFileView()->updateView(item);
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->sort(mFileThumbnailView->sortDirection());
}
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<FilterMode>( 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) {
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(updateViewItem(const KFileItem *)) );
d->mMetaInfoTodo.clear();
} else {
d->mMetaInfoJob = NULL;
}
}
void FileViewController::stopMetaInfoUpdate() {
d->mMetaInfoTodo.clear();
if(d->mMetaInfoJob) {
d->mMetaInfoJob->kill();
d->mMetaInfoJob = 0;
}
}
} // namespace