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.
790 lines
27 KiB
790 lines
27 KiB
15 years ago
|
/***************************************************************************
|
||
|
* Copyright (C) 2006 by Peter Penz *
|
||
|
* peter.penz@gmx.at *
|
||
|
* *
|
||
|
* 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. *
|
||
|
***************************************************************************/
|
||
|
|
||
|
#include "dolphindetailsview.h"
|
||
|
|
||
|
#include <kurldrag.h>
|
||
|
#include <qpainter.h>
|
||
|
#include <qobjectlist.h>
|
||
|
#include <qheader.h>
|
||
|
#include <qclipboard.h>
|
||
|
#include <qpainter.h>
|
||
|
#include <klocale.h>
|
||
|
#include <kglobalsettings.h>
|
||
|
#include <qscrollbar.h>
|
||
|
#include <qcursor.h>
|
||
|
#include <qstyle.h>
|
||
|
#include <assert.h>
|
||
|
|
||
|
#include "dolphinview.h"
|
||
|
#include "viewproperties.h"
|
||
|
#include "dolphin.h"
|
||
|
#include "kiconeffect.h"
|
||
|
#include "dolphinsettings.h"
|
||
|
#include "dolphinstatusbar.h"
|
||
|
#include "dolphindetailsviewsettings.h"
|
||
|
|
||
|
DolphinDetailsView::DolphinDetailsView(DolphinView* parent) :
|
||
|
KFileDetailView(parent, 0),
|
||
|
m_dolphinView(parent),
|
||
|
m_resizeTimer(0),
|
||
|
m_scrollTimer(0),
|
||
|
m_rubber(0)
|
||
|
{
|
||
|
m_resizeTimer = new QTimer(this);
|
||
|
connect(m_resizeTimer, SIGNAL(timeout()),
|
||
|
this, SLOT(updateColumnsWidth()));
|
||
|
|
||
|
setAcceptDrops(true);
|
||
|
setSelectionMode(KFile::Extended);
|
||
|
setHScrollBarMode(QScrollView::AlwaysOff);
|
||
|
|
||
|
setColumnAlignment(SizeColumn, Qt::AlignRight);
|
||
|
for (int i = DateColumn; i <= GroupColumn; ++i) {
|
||
|
setColumnAlignment(i, Qt::AlignHCenter);
|
||
|
}
|
||
|
|
||
|
Dolphin& dolphin = Dolphin::mainWin();
|
||
|
|
||
|
connect(this, SIGNAL(onItem(QListViewItem*)),
|
||
|
this, SLOT(slotOnItem(QListViewItem*)));
|
||
|
connect(this, SIGNAL(onViewport()),
|
||
|
this, SLOT(slotOnViewport()));
|
||
|
connect(this, SIGNAL(contextMenuRequested(QListViewItem*, const QPoint&, int)),
|
||
|
this, SLOT(slotContextMenuRequested(QListViewItem*, const QPoint&, int)));
|
||
|
connect(this, SIGNAL(selectionChanged()),
|
||
|
&dolphin, SLOT(slotSelectionChanged()));
|
||
|
connect(&dolphin, SIGNAL(activeViewChanged()),
|
||
|
this, SLOT(slotActivationUpdate()));
|
||
|
connect(this, SIGNAL(itemRenamed(QListViewItem*, const QString&, int)),
|
||
|
this, SLOT(slotItemRenamed(QListViewItem*, const QString&, int)));
|
||
|
connect(this, SIGNAL(dropped(QDropEvent*, const KURL::List&, const KURL&)),
|
||
|
parent, SLOT(slotURLListDropped(QDropEvent*, const KURL::List&, const KURL&)));
|
||
|
|
||
|
QClipboard* clipboard = QApplication::clipboard();
|
||
|
connect(clipboard, SIGNAL(dataChanged()),
|
||
|
this, SLOT(slotUpdateDisabledItems()));
|
||
|
|
||
|
QHeader* viewHeader = header();
|
||
|
viewHeader->setResizeEnabled(false);
|
||
|
viewHeader->setMovingEnabled(false);
|
||
|
connect(viewHeader, SIGNAL(clicked(int)),
|
||
|
this, SLOT(slotHeaderClicked(int)));
|
||
|
|
||
|
setMouseTracking(true);
|
||
|
setDefaultRenameAction(QListView::Accept);
|
||
|
|
||
|
refreshSettings();
|
||
|
}
|
||
|
|
||
|
DolphinDetailsView::~DolphinDetailsView()
|
||
|
{
|
||
|
delete m_rubber;
|
||
|
m_rubber = 0;
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::beginItemUpdates()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::endItemUpdates()
|
||
|
{
|
||
|
updateDisabledItems();
|
||
|
|
||
|
// Restore the current item. Use the information stored in the history if
|
||
|
// available. Otherwise use the first item as current item.
|
||
|
|
||
|
const KFileListViewItem* item = static_cast<const KFileListViewItem*>(firstChild());
|
||
|
if (item != 0) {
|
||
|
setCurrentItem(item->fileInfo());
|
||
|
}
|
||
|
|
||
|
int index = 0;
|
||
|
const QValueList<URLNavigator::HistoryElem> history = m_dolphinView->urlHistory(index);
|
||
|
if (!history.isEmpty()) {
|
||
|
KFileView* fileView = static_cast<KFileView*>(this);
|
||
|
fileView->setCurrentItem(history[index].currentFileName());
|
||
|
setContentsPos(history[index].contentsX(), history[index].contentsY());
|
||
|
}
|
||
|
|
||
|
updateColumnsWidth();
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::insertItem(KFileItem* fileItem)
|
||
|
{
|
||
|
KFileView::insertItem(fileItem);
|
||
|
|
||
|
DolphinListViewItem* item = new DolphinListViewItem(static_cast<QListView*>(this), fileItem);
|
||
|
|
||
|
QDir::SortSpec spec = KFileView::sorting();
|
||
|
if (spec & QDir::Time) {
|
||
|
item->setKey(sortingKey(fileItem->time(KIO::UDS_MODIFICATION_TIME),
|
||
|
fileItem->isDir(),
|
||
|
spec));
|
||
|
}
|
||
|
else if (spec & QDir::Size) {
|
||
|
item->setKey(sortingKey(fileItem->size(), fileItem->isDir(), spec));
|
||
|
}
|
||
|
else {
|
||
|
item->setKey(sortingKey(fileItem->text(), fileItem->isDir(), spec));
|
||
|
}
|
||
|
|
||
|
fileItem->setExtraData(this, item);
|
||
|
}
|
||
|
|
||
|
bool DolphinDetailsView::isOnFilename(const QListViewItem* item, const QPoint& pos) const
|
||
|
{
|
||
|
const QPoint absPos(mapToGlobal(QPoint(0, 0)));
|
||
|
return (pos.x() - absPos.x()) <= filenameWidth(item);
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::refreshSettings()
|
||
|
{
|
||
|
const DolphinDetailsViewSettings* settings = DolphinSettings::instance().detailsView();
|
||
|
assert(settings != 0);
|
||
|
|
||
|
for (int i = DolphinDetailsView::GroupColumn; i >= DolphinDetailsView::NameColumn; --i) {
|
||
|
if (!settings->isColumnEnabled(i)) {
|
||
|
removeColumn(i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QFont adjustedFont(font());
|
||
|
adjustedFont.setFamily(settings->fontFamily());
|
||
|
adjustedFont.setPointSize(settings->fontSize());
|
||
|
setFont(adjustedFont);
|
||
|
|
||
|
updateView(true);
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::zoomIn()
|
||
|
{
|
||
|
if (isZoomInPossible()) {
|
||
|
DolphinDetailsViewSettings* settings = DolphinSettings::instance().detailsView();
|
||
|
switch (settings->iconSize()) {
|
||
|
case KIcon::SizeSmall: settings->setIconSize(KIcon::SizeMedium); break;
|
||
|
case KIcon::SizeMedium: settings->setIconSize(KIcon::SizeLarge); break;
|
||
|
default: assert(false); break;
|
||
|
}
|
||
|
ItemEffectsManager::zoomIn();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::zoomOut()
|
||
|
{
|
||
|
if (isZoomOutPossible()) {
|
||
|
DolphinDetailsViewSettings* settings = DolphinSettings::instance().detailsView();
|
||
|
switch (settings->iconSize()) {
|
||
|
case KIcon::SizeLarge: settings->setIconSize(KIcon::SizeMedium); break;
|
||
|
case KIcon::SizeMedium: settings->setIconSize(KIcon::SizeSmall); break;
|
||
|
default: assert(false); break;
|
||
|
}
|
||
|
ItemEffectsManager::zoomOut();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool DolphinDetailsView::isZoomInPossible() const
|
||
|
{
|
||
|
DolphinDetailsViewSettings* settings = DolphinSettings::instance().detailsView();
|
||
|
return settings->iconSize() < KIcon::SizeLarge;
|
||
|
}
|
||
|
|
||
|
bool DolphinDetailsView::isZoomOutPossible() const
|
||
|
{
|
||
|
DolphinDetailsViewSettings* settings = DolphinSettings::instance().detailsView();
|
||
|
return settings->iconSize() > KIcon::SizeSmall;
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::resizeContents(int width, int height)
|
||
|
{
|
||
|
KFileDetailView::resizeContents(width, height);
|
||
|
|
||
|
// When loading several 1000 items a punch of resize events
|
||
|
// drops in. As updating the column width is a quite expensive
|
||
|
// operation, this operation will be postponed until there is
|
||
|
// no resize event for at least 50 milliseconds.
|
||
|
m_resizeTimer->stop();
|
||
|
m_resizeTimer->start(50, true);
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::slotOnItem(QListViewItem* item)
|
||
|
{
|
||
|
if (isOnFilename(item, QCursor::pos())) {
|
||
|
activateItem(item);
|
||
|
KFileItem* fileItem = static_cast<KFileListViewItem*>(item)->fileInfo();
|
||
|
m_dolphinView->requestItemInfo(fileItem->url());
|
||
|
}
|
||
|
else {
|
||
|
resetActivatedItem();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::slotOnViewport()
|
||
|
{
|
||
|
resetActivatedItem();
|
||
|
m_dolphinView->requestItemInfo(KURL());
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::setContextPixmap(void* context,
|
||
|
const QPixmap& pixmap)
|
||
|
{
|
||
|
reinterpret_cast<KFileListViewItem*>(context)->setPixmap(0, pixmap);
|
||
|
}
|
||
|
|
||
|
const QPixmap* DolphinDetailsView::contextPixmap(void* context)
|
||
|
{
|
||
|
return reinterpret_cast<KFileListViewItem*>(context)->pixmap(0);
|
||
|
}
|
||
|
|
||
|
void* DolphinDetailsView::firstContext()
|
||
|
{
|
||
|
return reinterpret_cast<void*>(firstChild());
|
||
|
}
|
||
|
|
||
|
void* DolphinDetailsView::nextContext(void* context)
|
||
|
{
|
||
|
KFileListViewItem* listViewItem = reinterpret_cast<KFileListViewItem*>(context);
|
||
|
return reinterpret_cast<void*>(listViewItem->nextSibling());
|
||
|
}
|
||
|
|
||
|
KFileItem* DolphinDetailsView::contextFileInfo(void* context)
|
||
|
{
|
||
|
return reinterpret_cast<KFileListViewItem*>(context)->fileInfo();
|
||
|
}
|
||
|
|
||
|
|
||
|
void DolphinDetailsView::contentsDragMoveEvent(QDragMoveEvent* event)
|
||
|
{
|
||
|
KFileDetailView::contentsDragMoveEvent(event);
|
||
|
|
||
|
// If a dragging is done above a directory, show the icon as 'active' for
|
||
|
// a visual feedback
|
||
|
KFileListViewItem* item = static_cast<KFileListViewItem*>(itemAt(event->pos()));
|
||
|
|
||
|
bool showActive = false;
|
||
|
if (item != 0) {
|
||
|
const KFileItem* fileInfo = item->fileInfo();
|
||
|
showActive = (fileInfo != 0) && fileInfo->isDir();
|
||
|
}
|
||
|
|
||
|
if (showActive) {
|
||
|
slotOnItem(item);
|
||
|
}
|
||
|
else {
|
||
|
slotOnViewport();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::resizeEvent(QResizeEvent* event)
|
||
|
{
|
||
|
KFileDetailView::resizeEvent(event);
|
||
|
|
||
|
// When loading several 1000 items a punch of resize events
|
||
|
// drops in. As updating the column width is a quite expensive
|
||
|
// operation, this operation will be postponed until there is
|
||
|
// no resize event for at least 50 milliseconds.
|
||
|
m_resizeTimer->stop();
|
||
|
m_resizeTimer->start(50, true);
|
||
|
}
|
||
|
|
||
|
bool DolphinDetailsView::acceptDrag(QDropEvent* event) const
|
||
|
{
|
||
|
bool accept = KURLDrag::canDecode(event) &&
|
||
|
(event->action() == QDropEvent::Copy ||
|
||
|
event->action() == QDropEvent::Move ||
|
||
|
event->action() == QDropEvent::Link);
|
||
|
if (accept) {
|
||
|
if (static_cast<const QWidget*>(event->source()) == this) {
|
||
|
KFileListViewItem* item = static_cast<KFileListViewItem*>(itemAt(event->pos()));
|
||
|
accept = (item != 0);
|
||
|
if (accept) {
|
||
|
KFileItem* fileItem = item->fileInfo();
|
||
|
accept = fileItem->isDir();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return accept;
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::contentsDropEvent(QDropEvent* event)
|
||
|
{
|
||
|
// KFileDetailView::contentsDropEvent does not care whether the mouse
|
||
|
// cursor is above a filename or not, the destination URL is always
|
||
|
// the URL of the item. This is fixed here in a way that the destination
|
||
|
// URL is only the URL of the item if the cursor is above the filename.
|
||
|
const QPoint pos(QCursor::pos());
|
||
|
const QPoint viewportPos(viewport()->mapToGlobal(QPoint(0, 0)));
|
||
|
QListViewItem* item = itemAt(QPoint(pos.x() - viewportPos.x(), pos.y() - viewportPos.y()));
|
||
|
if ((item == 0) || ((item != 0) && isOnFilename(item, pos))) {
|
||
|
// dropping is done on the viewport or directly above a filename
|
||
|
KFileDetailView::contentsDropEvent(event);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Dropping is done above an item, but the mouse cursor is not above the file name.
|
||
|
// In this case the signals of the base implementation will be blocked and send
|
||
|
// in a corrected manner afterwards.
|
||
|
assert(item != 0);
|
||
|
const bool block = signalsBlocked();
|
||
|
blockSignals(true);
|
||
|
KFileDetailView::contentsDropEvent(event);
|
||
|
blockSignals(block);
|
||
|
|
||
|
if (!acceptDrag(event)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
emit dropped(event, 0);
|
||
|
KURL::List urls;
|
||
|
if (KURLDrag::decode(event, urls) && !urls.isEmpty()) {
|
||
|
emit dropped(event, urls, KURL());
|
||
|
sig->dropURLs(0, event, urls);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::contentsMousePressEvent(QMouseEvent* event)
|
||
|
{
|
||
|
if (m_rubber != 0) {
|
||
|
drawRubber();
|
||
|
delete m_rubber;
|
||
|
m_rubber = 0;
|
||
|
}
|
||
|
|
||
|
// Swallow the base implementation of the mouse press event
|
||
|
// if the mouse cursor is not above the filename. This prevents
|
||
|
// that the item gets selected and simulates an equal usability
|
||
|
// like in the icon view.
|
||
|
const QPoint pos(QCursor::pos());
|
||
|
const QPoint viewportPos(viewport()->mapToGlobal(QPoint(0, 0)));
|
||
|
QListViewItem* item = itemAt(QPoint(pos.x() - viewportPos.x(), pos.y() - viewportPos.y()));
|
||
|
if ((item != 0) && isOnFilename(item, pos)) {
|
||
|
KFileDetailView::contentsMousePressEvent(event);
|
||
|
}
|
||
|
else if (event->button() == Qt::LeftButton) {
|
||
|
const ButtonState keyboardState = KApplication::keyboardMouseState();
|
||
|
const bool isSelectionActive = (keyboardState & ShiftButton) ||
|
||
|
(keyboardState & ControlButton);
|
||
|
if (!isSelectionActive) {
|
||
|
clearSelection();
|
||
|
}
|
||
|
|
||
|
assert(m_rubber == 0);
|
||
|
m_rubber = new QRect(event->x(), event->y(), 0, 0);
|
||
|
}
|
||
|
|
||
|
resetActivatedItem();
|
||
|
emit signalRequestActivation();
|
||
|
|
||
|
m_dolphinView->statusBar()->clear();
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::contentsMouseMoveEvent(QMouseEvent* event)
|
||
|
{
|
||
|
if (m_rubber != 0) {
|
||
|
slotAutoScroll();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
KFileDetailView::contentsMouseMoveEvent(event);
|
||
|
|
||
|
const QPoint& pos = event->globalPos();
|
||
|
const QPoint viewportPos = viewport()->mapToGlobal(QPoint(0, 0));
|
||
|
QListViewItem* item = itemAt(QPoint(pos.x() - viewportPos.x(), pos.y() - viewportPos.y()));
|
||
|
if ((item != 0) && isOnFilename(item, pos)) {
|
||
|
activateItem(item);
|
||
|
}
|
||
|
else {
|
||
|
resetActivatedItem();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::contentsMouseReleaseEvent(QMouseEvent* event)
|
||
|
{
|
||
|
if (m_rubber != 0) {
|
||
|
drawRubber();
|
||
|
delete m_rubber;
|
||
|
m_rubber = 0;
|
||
|
}
|
||
|
|
||
|
if (m_scrollTimer != 0) {
|
||
|
disconnect(m_scrollTimer, SIGNAL(timeout()),
|
||
|
this, SLOT(slotAutoScroll()));
|
||
|
m_scrollTimer->stop();
|
||
|
delete m_scrollTimer;
|
||
|
m_scrollTimer = 0;
|
||
|
}
|
||
|
|
||
|
KFileDetailView::contentsMouseReleaseEvent(event);
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::paintEmptyArea(QPainter* painter, const QRect& rect)
|
||
|
{
|
||
|
if (m_dolphinView->isActive()) {
|
||
|
KFileDetailView::paintEmptyArea(painter, rect);
|
||
|
}
|
||
|
else {
|
||
|
const QBrush brush(colorGroup().background());
|
||
|
painter->fillRect(rect, brush);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::drawRubber()
|
||
|
{
|
||
|
// Parts of the following code have been taken
|
||
|
// from the class KonqBaseListViewWidget located in
|
||
|
// konqueror/listview/konq_listviewwidget.h of Konqueror.
|
||
|
// (Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
|
||
|
// 2001, 2002, 2004 Michael Brade <brade@kde.org>)
|
||
|
if (m_rubber == 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
QPainter p;
|
||
|
p.begin(viewport());
|
||
|
p.setRasterOp(NotROP);
|
||
|
p.setPen(QPen(color0, 1));
|
||
|
p.setBrush(NoBrush);
|
||
|
|
||
|
QPoint point(m_rubber->x(), m_rubber->y());
|
||
|
point = contentsToViewport(point);
|
||
|
style().drawPrimitive(QStyle::PE_FocusRect, &p,
|
||
|
QRect(point.x(), point.y(), m_rubber->width(), m_rubber->height()),
|
||
|
colorGroup(), QStyle::Style_Default, colorGroup().base());
|
||
|
p.end();
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::viewportPaintEvent(QPaintEvent* paintEvent)
|
||
|
{
|
||
|
drawRubber();
|
||
|
KFileDetailView::viewportPaintEvent(paintEvent);
|
||
|
drawRubber();
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::leaveEvent(QEvent* event)
|
||
|
{
|
||
|
KFileDetailView::leaveEvent(event);
|
||
|
slotOnViewport();
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::slotActivationUpdate()
|
||
|
{
|
||
|
update();
|
||
|
|
||
|
// TODO: there must be a simpler way to say
|
||
|
// "update all children"
|
||
|
const QObjectList* list = children();
|
||
|
if (list == 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
QObjectListIterator it(*list);
|
||
|
QObject* object = 0;
|
||
|
while ((object = it.current()) != 0) {
|
||
|
if (object->inherits("QWidget")) {
|
||
|
QWidget* widget = static_cast<QWidget*>(object);
|
||
|
widget->update();
|
||
|
}
|
||
|
++it;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::slotContextMenuRequested(QListViewItem* item,
|
||
|
const QPoint& pos,
|
||
|
int /* col */)
|
||
|
{
|
||
|
KFileItem* fileInfo = 0;
|
||
|
if ((item != 0) && isOnFilename(item, pos)) {
|
||
|
fileInfo = static_cast<KFileListViewItem*>(item)->fileInfo();
|
||
|
}
|
||
|
m_dolphinView->openContextMenu(fileInfo, pos);
|
||
|
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::slotUpdateDisabledItems()
|
||
|
{
|
||
|
updateDisabledItems();
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::slotAutoScroll()
|
||
|
{
|
||
|
// Parts of the following code have been taken
|
||
|
// from the class KonqBaseListViewWidget located in
|
||
|
// konqueror/listview/konq_listviewwidget.h of Konqueror.
|
||
|
// (Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
|
||
|
// 2001, 2002, 2004 Michael Brade <brade@kde.org>)
|
||
|
|
||
|
const QPoint pos(viewport()->mapFromGlobal(QCursor::pos()));
|
||
|
const QPoint vc(viewportToContents(pos));
|
||
|
|
||
|
if (vc == m_rubber->bottomRight()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
drawRubber();
|
||
|
|
||
|
m_rubber->setBottomRight(vc);
|
||
|
|
||
|
QListViewItem* item = itemAt(QPoint(0,0));
|
||
|
|
||
|
const bool block = signalsBlocked();
|
||
|
blockSignals(true);
|
||
|
|
||
|
const QRect rubber(m_rubber->normalize());
|
||
|
const int bottom = contentsY() + visibleHeight() - 1;
|
||
|
|
||
|
// select all items which intersect with the rubber, deselect all others
|
||
|
bool bottomReached = false;
|
||
|
while ((item != 0) && !bottomReached) {
|
||
|
QRect rect(itemRect(item));
|
||
|
rect.setWidth(filenameWidth(item));
|
||
|
rect = QRect(viewportToContents(rect.topLeft()),
|
||
|
viewportToContents(rect.bottomRight()));
|
||
|
if (rect.isValid() && (rect.top() <= bottom)) {
|
||
|
const KFileItem* fileItem = static_cast<KFileListViewItem*>(item)->fileInfo();
|
||
|
setSelected(fileItem, rect.intersects(rubber));
|
||
|
item = item->itemBelow();
|
||
|
}
|
||
|
else {
|
||
|
bottomReached = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
blockSignals(block);
|
||
|
emit selectionChanged();
|
||
|
|
||
|
drawRubber();
|
||
|
|
||
|
// scroll the viewport if the top or bottom margin is reached
|
||
|
const int scrollMargin = 40;
|
||
|
ensureVisible(vc.x(), vc.y(), scrollMargin, scrollMargin);
|
||
|
const bool scroll = !QRect(scrollMargin,
|
||
|
scrollMargin,
|
||
|
viewport()->width() - 2 * scrollMargin,
|
||
|
viewport()->height() - 2 * scrollMargin).contains(pos);
|
||
|
if (scroll) {
|
||
|
if (m_scrollTimer == 0) {
|
||
|
m_scrollTimer = new QTimer( this );
|
||
|
connect(m_scrollTimer, SIGNAL(timeout()),
|
||
|
this, SLOT(slotAutoScroll()));
|
||
|
m_scrollTimer->start(100, false);
|
||
|
}
|
||
|
}
|
||
|
else if (m_scrollTimer != 0) {
|
||
|
disconnect(m_scrollTimer, SIGNAL(timeout()),
|
||
|
this, SLOT(slotAutoScroll()));
|
||
|
m_scrollTimer->stop();
|
||
|
delete m_scrollTimer;
|
||
|
m_scrollTimer = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::updateColumnsWidth()
|
||
|
{
|
||
|
const int columnCount = columns();
|
||
|
int requiredWidth = 0;
|
||
|
for (int i = 1; i < columnCount; ++i) {
|
||
|
// When a directory contains no items, a minimum width for
|
||
|
// the column must be available, so that the header is readable.
|
||
|
// TODO: use header data instead of the hardcoded 64 value...
|
||
|
int columnWidth = 64;
|
||
|
QFontMetrics fontMetrics(font());
|
||
|
for (QListViewItem* item = firstChild(); item != 0; item = item->nextSibling()) {
|
||
|
const int width = item->width(fontMetrics, this, i);
|
||
|
if (width > columnWidth) {
|
||
|
columnWidth = width;
|
||
|
}
|
||
|
}
|
||
|
columnWidth += 16; // add custom margin
|
||
|
setColumnWidth(i, columnWidth);
|
||
|
requiredWidth += columnWidth;
|
||
|
}
|
||
|
|
||
|
// resize the first column in a way that the
|
||
|
// whole available width is used
|
||
|
int firstColumnWidth = visibleWidth() - requiredWidth;
|
||
|
if (firstColumnWidth < 128) {
|
||
|
firstColumnWidth = 128;
|
||
|
}
|
||
|
setColumnWidth(0, firstColumnWidth);
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::slotItemRenamed(QListViewItem* item,
|
||
|
const QString& name,
|
||
|
int /* column */)
|
||
|
{
|
||
|
KFileItem* fileInfo = static_cast<KFileListViewItem*>(item)->fileInfo();
|
||
|
m_dolphinView->rename(KURL(fileInfo->url()), name);
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::slotHeaderClicked(int /* section */)
|
||
|
{
|
||
|
// The sorting has already been changed in QListView if this slot is
|
||
|
// invoked, but Dolphin was not informed about this (no signal is available
|
||
|
// which indicates a change of the sorting). This is bypassed by changing
|
||
|
// the sorting and sort order to a temporary other value and readjust it again.
|
||
|
const int column = sortColumn();
|
||
|
if (column <= DateColumn) {
|
||
|
DolphinView::Sorting sorting = DolphinView::SortByName;
|
||
|
switch (column) {
|
||
|
case SizeColumn: sorting = DolphinView::SortBySize; break;
|
||
|
case DateColumn: sorting = DolphinView::SortByDate; break;
|
||
|
case NameColumn:
|
||
|
default: break;
|
||
|
}
|
||
|
|
||
|
const Qt::SortOrder currSortOrder = sortOrder();
|
||
|
|
||
|
// temporary adjust the sorting and sort order to different values...
|
||
|
const DolphinView::Sorting tempSorting = (sorting == DolphinView::SortByName) ?
|
||
|
DolphinView::SortBySize :
|
||
|
DolphinView::SortByName;
|
||
|
m_dolphinView->setSorting(tempSorting);
|
||
|
const Qt::SortOrder tempSortOrder = (currSortOrder == Qt::Ascending) ?
|
||
|
Qt::Descending : Qt::Ascending;
|
||
|
m_dolphinView->setSortOrder(tempSortOrder);
|
||
|
|
||
|
// ... so that setting them again results in storing the new setting.
|
||
|
m_dolphinView->setSorting(sorting);
|
||
|
m_dolphinView->setSortOrder(currSortOrder);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DolphinDetailsView::DolphinListViewItem::DolphinListViewItem(QListView* parent,
|
||
|
KFileItem* fileItem) :
|
||
|
KFileListViewItem(parent, fileItem)
|
||
|
{
|
||
|
const int iconSize = DolphinSettings::instance().detailsView()->iconSize();
|
||
|
KFileItem* info = fileInfo();
|
||
|
setPixmap(DolphinDetailsView::NameColumn, info->pixmap(iconSize));
|
||
|
|
||
|
// The base class KFileListViewItem represents the column 'Size' only as byte values.
|
||
|
// Adjust those values in a way that a mapping to GBytes, MBytes, KBytes and Bytes
|
||
|
// is done. As the file size for directories is useless (only the size of the directory i-node
|
||
|
// is given), it is removed completely.
|
||
|
if (fileItem->isDir()) {
|
||
|
setText(SizeColumn, " - ");
|
||
|
}
|
||
|
else {
|
||
|
QString sizeText(KIO::convertSize(fileItem->size()));
|
||
|
sizeText.append(" ");
|
||
|
setText(SizeColumn, sizeText);
|
||
|
}
|
||
|
|
||
|
// Dolphin allows to remove specific columns, but the base class KFileListViewItem
|
||
|
// is not aware about this (or at least the class KFileDetailView does not react on
|
||
|
// QListView::remove()). Therefore the columns are rearranged here.
|
||
|
const DolphinDetailsViewSettings* settings = DolphinSettings::instance().detailsView();
|
||
|
assert(settings != 0);
|
||
|
|
||
|
int column_idx = DateColumn; // the columns for 'name' and 'size' cannot get removed
|
||
|
for (int i = DolphinDetailsView::DateColumn; i <= DolphinDetailsView::GroupColumn; ++i) {
|
||
|
if (column_idx < i) {
|
||
|
setText(column_idx, text(i));
|
||
|
}
|
||
|
if (settings->isColumnEnabled(i)) {
|
||
|
++column_idx;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DolphinDetailsView::DolphinListViewItem::~DolphinListViewItem()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::DolphinListViewItem::paintCell(QPainter* painter,
|
||
|
const QColorGroup& colorGroup,
|
||
|
int column,
|
||
|
int cellWidth,
|
||
|
int alignment)
|
||
|
{
|
||
|
const QListView* view = listView();
|
||
|
const bool isActive = view->parent() == Dolphin::mainWin().activeView();
|
||
|
if (isSelected()) {
|
||
|
// Per default the selection is drawn above the whole width of the item. As a consistent
|
||
|
// behavior with the icon view is wanted, only the the column containing the file name
|
||
|
// should be shown as selected.
|
||
|
QColorGroup defaultColorGroup(colorGroup);
|
||
|
const QColor highlightColor(isActive ? backgroundColor(column) : view->colorGroup().background());
|
||
|
defaultColorGroup.setColor(QColorGroup::Highlight , highlightColor);
|
||
|
defaultColorGroup.setColor(QColorGroup::HighlightedText, colorGroup.color(QColorGroup::Text));
|
||
|
KFileListViewItem::paintCell(painter, defaultColorGroup, column, cellWidth, alignment);
|
||
|
|
||
|
if (column == 0) {
|
||
|
// draw the selection only on the first column
|
||
|
QListView* parent = listView();
|
||
|
const int itemWidth = width(parent->fontMetrics(), parent, 0);
|
||
|
if (isActive) {
|
||
|
KFileListViewItem::paintCell(painter, colorGroup, column, itemWidth, alignment);
|
||
|
}
|
||
|
else {
|
||
|
QListViewItem::paintCell(painter, colorGroup, column, itemWidth, alignment);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (isActive) {
|
||
|
KFileListViewItem::paintCell(painter, colorGroup, column, cellWidth, alignment);
|
||
|
}
|
||
|
else {
|
||
|
QListViewItem::paintCell(painter, colorGroup, column, cellWidth, alignment);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (column < listView()->columns() - 1) {
|
||
|
// draw a separator between columns
|
||
|
painter->setPen(KGlobalSettings::buttonBackground());
|
||
|
painter->drawLine(cellWidth - 1, 0, cellWidth - 1, height() - 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DolphinDetailsView::DolphinListViewItem::paintFocus(QPainter* painter,
|
||
|
const QColorGroup& colorGroup,
|
||
|
const QRect& rect)
|
||
|
{
|
||
|
// draw the focus consistently with the selection (see implementation notes
|
||
|
// in DolphinListViewItem::paintCell)
|
||
|
QListView* parent = listView();
|
||
|
int visibleWidth = width(parent->fontMetrics(), parent, 0);
|
||
|
const int colWidth = parent->columnWidth(0);
|
||
|
if (visibleWidth > colWidth) {
|
||
|
visibleWidth = colWidth;
|
||
|
}
|
||
|
|
||
|
QRect focusRect(rect);
|
||
|
focusRect.setWidth(visibleWidth);
|
||
|
|
||
|
KFileListViewItem::paintFocus(painter, colorGroup, focusRect);
|
||
|
}
|
||
|
|
||
|
int DolphinDetailsView::filenameWidth(const QListViewItem* item) const
|
||
|
{
|
||
|
assert(item != 0);
|
||
|
|
||
|
int visibleWidth = item->width(fontMetrics(), this, 0);
|
||
|
const int colWidth = columnWidth(0);
|
||
|
if (visibleWidth > colWidth) {
|
||
|
visibleWidth = colWidth;
|
||
|
}
|
||
|
|
||
|
return visibleWidth;
|
||
|
}
|
||
|
#include "dolphindetailsview.moc"
|