Merged very early alpha-version of Dolphin 2.0

Dolphin 2.0 will get a new view-engine with the
following improvements:
- Better performance
- Animated transitions
- No clipped filenames due to dynamic item-sizes
- Grouping support for all view-modes
- Non-rectangular selection areas
- Simplified code for better maintenance

More details will be provided in a blog-entry during
the next days.

Please note that the code is in a very
early alpha-stage and although the most tricky parts
have been implemented already very basic things like
drag and drop or selections have not been pushed yet.
Those things are rather trivial to implement but this
still will take some time.
This commit is contained in:
Peter Penz 2011-07-30 20:13:09 +02:00
parent 69e4007e5e
commit f23e9496f3
130 changed files with 9620 additions and 10430 deletions

View file

@ -3,6 +3,9 @@ macro_log_feature(Nepomuk_FOUND "Nepomuk" "Nepomuk" "http://www.kde.org" FALSE "
macro_bool_to_01(Nepomuk_FOUND HAVE_NEPOMUK)
configure_file(config-nepomuk.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-nepomuk.h )
macro_bool_to_01(X11_Xrender_FOUND HAVE_XRENDER)
configure_file(config-X11.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-X11.h )
include_directories( ${KDE4_INCLUDE_DIR} ${QT_INCLUDES} )
if (Nepomuk_FOUND)
@ -15,47 +18,49 @@ add_subdirectory(tests)
########### next target ###############
set(dolphinprivate_LIB_SRCS
kitemviews/kfileitemlistview.cpp
kitemviews/kfileitemlistwidget.cpp
kitemviews/kfileitemmodel.cpp
kitemviews/kfileitemmodelrolesupdater.cpp
kitemviews/kitemlistcontainer.cpp
kitemviews/kitemlistcontroller.cpp
kitemviews/kitemlistgroupheader.cpp
kitemviews/kitemlistselectionmanager.cpp
kitemviews/kitemlistsizehintresolver.cpp
kitemviews/kitemliststyleoption.cpp
kitemviews/kitemlistview.cpp
kitemviews/kitemlistviewanimation.cpp
kitemviews/kitemlistviewlayouter.cpp
kitemviews/kitemlistwidget.cpp
kitemviews/kitemmodelbase.cpp
kitemviews/kpixmapmodifier.cpp
settings/additionalinfodialog.cpp
settings/applyviewpropsjob.cpp
settings/dolphinsettings.cpp
settings/viewpropertiesdialog.cpp
settings/viewpropsprogressinfo.cpp
views/additionalinfoaccessor.cpp
views/dolphincategorydrawer.cpp
views/dolphindirlister.cpp
views/dolphinview.cpp
views/dolphindetailsview.cpp
views/dolphindetailsviewexpander.cpp
views/dolphinfileitemdelegate.cpp
views/dolphiniconsview.cpp
views/dolphincolumnview.cpp
views/dolphincolumnviewcontainer.cpp
views/dolphinmodel.cpp
views/dolphinitemlistcontainer.cpp
views/dolphinnewfilemenuobserver.cpp
views/dolphinremoteencoding.cpp
views/dolphinsortfilterproxymodel.cpp
views/dolphintreeview.cpp
views/dolphinviewactionhandler.cpp
views/dolphinviewautoscroller.cpp
views/dolphinviewcontroller.cpp
views/draganddrophelper.cpp
views/folderexpander.cpp
views/renamedialog.cpp
views/selectiontoggle.cpp
views/selectionmanager.cpp
views/tooltips/filemetadatatooltip.cpp
views/tooltips/tooltipmanager.cpp
views/versioncontrol/pendingthreadsmaintainer.cpp
views/versioncontrol/updateitemstatesthread.cpp
views/versioncontrol/versioncontrolobserver.cpp
views/viewextensionsfactory.cpp
views/viewmodecontroller.cpp
views/viewproperties.cpp
views/zoomlevelinfo.cpp
)
kde4_add_kcfg_files(dolphinprivate_LIB_SRCS
settings/dolphin_columnmodesettings.kcfgc
settings/dolphin_compactmodesettings.kcfgc
settings/dolphin_directoryviewpropertysettings.kcfgc
settings/dolphin_detailsmodesettings.kcfgc
settings/dolphin_iconsmodesettings.kcfgc
@ -69,6 +74,10 @@ target_link_libraries(dolphinprivate ${KDE4_KFILE_LIBS} konq ${KDE4_KNEWSTUFF3_L
if (Nepomuk_FOUND)
target_link_libraries(dolphinprivate ${NEPOMUK_LIBRARIES} ${NEPOMUK_QUERY_LIBRARIES} nepomukutils ${SOPRANO_LIBRARIES})
endif (Nepomuk_FOUND)
if(X11_Xrender_FOUND)
target_link_libraries(dolphinprivate ${X11_Xrender_LIB} )
endif(X11_Xrender_FOUND)
set_target_properties(dolphinprivate PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
@ -129,7 +138,6 @@ set(dolphin_SRCS
settings/servicemodel.cpp
settings/startup/startupsettingspage.cpp
settings/trash/trashsettingspage.cpp
settings/viewmodes/columnviewsettingspage.cpp
settings/viewmodes/detailsviewsettingspage.cpp
settings/viewmodes/dolphinfontrequester.cpp
settings/viewmodes/iconsizegroupbox.cpp
@ -144,6 +152,9 @@ set(dolphin_SRCS
kde4_add_kcfg_files(dolphin_SRCS
panels/folders/dolphin_folderspanelsettings.kcfgc
panels/information/dolphin_informationpanelsettings.kcfgc
settings/dolphin_compactmodesettings.kcfgc
settings/dolphin_detailsmodesettings.kcfgc
settings/dolphin_iconsmodesettings.kcfgc
search/dolphin_searchsettings.kcfgc
settings/dolphin_versioncontrolsettings.kcfgc
)
@ -190,7 +201,6 @@ install(TARGETS dolphin ${INSTALL_TARGETS_DEFAULT_ARGS})
set(kcm_dolphinviewmodes_PART_SRCS
settings/kcm/kcmdolphinviewmodes.cpp
settings/viewmodes/columnviewsettingspage.cpp
settings/viewmodes/detailsviewsettingspage.cpp
settings/viewmodes/dolphinfontrequester.cpp
settings/viewmodes/iconsizegroupbox.cpp
@ -221,7 +231,7 @@ set(kcm_dolphingeneral_PART_SRCS
settings/servicemodel.cpp)
kde4_add_kcfg_files(kcm_dolphinviewmodes_PART_SRCS
settings/dolphin_columnmodesettings.kcfgc
settings/dolphin_compactmodesettings.kcfgc
settings/dolphin_directoryviewpropertysettings.kcfgc
settings/dolphin_detailsmodesettings.kcfgc
settings/dolphin_iconsmodesettings.kcfgc
@ -273,7 +283,7 @@ install(TARGETS kio_filenamesearch DESTINATION ${PLUGIN_INSTALL_DIR})
install( PROGRAMS dolphin.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} )
install( FILES settings/dolphin_directoryviewpropertysettings.kcfg
settings/dolphin_generalsettings.kcfg
settings/dolphin_columnmodesettings.kcfg
settings/dolphin_compactmodesettings.kcfg
settings/dolphin_iconsmodesettings.kcfg
settings/dolphin_detailsmodesettings.kcfg
settings/dolphin_versioncontrolsettings.kcfg

1
src/config-X11.h.cmake Normal file
View file

@ -0,0 +1 @@
#cmakedefine HAVE_XRENDER 1

View file

@ -234,7 +234,7 @@ void DolphinContextMenu::openItemContextMenu()
// setup 'Create New' menu
DolphinNewFileMenu* newFileMenu = new DolphinNewFileMenu(m_mainWindow);
const DolphinView* view = m_mainWindow->activeViewContainer()->view();
newFileMenu->setViewShowsHiddenFiles(view->showHiddenFiles());
newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown());
newFileMenu->checkUpToDate();
newFileMenu->setPopupFiles(m_fileInfo.url());
newFileMenu->setEnabled(selectedItemsProperties().supportsWriting());
@ -320,7 +320,7 @@ void DolphinContextMenu::openViewportContextMenu()
// setup 'Create New' menu
KNewFileMenu* newFileMenu = m_mainWindow->newFileMenu();
const DolphinView* view = m_mainWindow->activeViewContainer()->view();
newFileMenu->setViewShowsHiddenFiles(view->showHiddenFiles());
newFileMenu->setViewShowsHiddenFiles(view->hiddenFilesShown());
newFileMenu->checkUpToDate();
newFileMenu->setPopupFiles(m_baseUrl);
m_popup->addMenu(newFileMenu->menu());

View file

@ -42,7 +42,6 @@
#include "statusbar/dolphinstatusbar.h"
#include "views/dolphinviewactionhandler.h"
#include "views/dolphinremoteencoding.h"
#include "views/draganddrophelper.h"
#include "views/viewproperties.h"
#ifndef Q_OS_WIN
@ -161,8 +160,8 @@ DolphinMainWindow::DolphinMainWindow() :
this, SLOT(showCommand(CommandType)));
connect(DolphinSettings::instance().placesModel(), SIGNAL(errorMessage(const QString&)),
this, SLOT(showErrorMessage(const QString&)));
connect(&DragAndDropHelper::instance(), SIGNAL(errorMessage(const QString&)),
this, SLOT(showErrorMessage(const QString&)));
//connect(&DragAndDropHelper::instance(), SIGNAL(errorMessage(const QString&)),
// this, SLOT(showErrorMessage(const QString&)));
const DolphinSettings& settings = DolphinSettings::instance();
@ -725,14 +724,14 @@ void DolphinMainWindow::readProperties(const KConfigGroup& group)
void DolphinMainWindow::updateNewMenu()
{
m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->showHiddenFiles());
m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown());
m_newFileMenu->checkUpToDate();
m_newFileMenu->setPopupFiles(activeViewContainer()->url());
}
void DolphinMainWindow::createDirectory()
{
m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->showHiddenFiles());
m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown());
m_newFileMenu->setPopupFiles(activeViewContainer()->url());
m_newFileMenu->createDirectory();
}
@ -1313,7 +1312,8 @@ void DolphinMainWindow::tabDropEvent(int tab, QDropEvent* event)
if (!urls.isEmpty() && tab != -1) {
const ViewTab& viewTab = m_viewTab[tab];
const KUrl destPath = viewTab.isPrimaryViewActive ? viewTab.primaryView->url() : viewTab.secondaryView->url();
DragAndDropHelper::instance().dropUrls(KFileItem(), destPath, event, m_tabBar);
Q_UNUSED(destPath);
//DragAndDropHelper::instance().dropUrls(KFileItem(), destPath, event, m_tabBar);
}
}
@ -2178,7 +2178,7 @@ void DolphinMainWindow::createSecondaryView(int tabIndex)
const int newWidth = (m_viewTab[tabIndex].primaryView->width() - splitter->handleWidth()) / 2;
const DolphinView* view = m_viewTab[tabIndex].primaryView->view();
m_viewTab[tabIndex].secondaryView = createViewContainer(view->rootUrl(), 0);
m_viewTab[tabIndex].secondaryView = createViewContainer(view->url(), 0);
splitter->addWidget(m_viewTab[tabIndex].secondaryView);
splitter->setSizes(QList<int>() << newWidth << newWidth);
connectViewSignals(m_viewTab[tabIndex].secondaryView);

View file

@ -44,8 +44,6 @@
#include "settings/dolphinsettings.h"
#include "views/dolphinview.h"
#include "views/dolphinviewactionhandler.h"
#include "views/dolphinsortfilterproxymodel.h"
#include "views/dolphinmodel.h"
#include "views/dolphinnewfilemenuobserver.h"
#include "views/dolphinremoteencoding.h"
#include "views/dolphindirlister.h"
@ -96,7 +94,7 @@ DolphinPart::DolphinPart(QWidget* parentWidget, QObject* parent, const QVariantL
this, SLOT(slotSelectionChanged(KFileItemList)));
connect(m_view, SIGNAL(requestItemInfo(KFileItem)),
this, SLOT(slotRequestItemInfo(KFileItem)));
connect(m_view, SIGNAL(modeChanged()),
connect(m_view, SIGNAL(modeChanged(DolphinView::Mode,DolphinView::Mode)),
this, SIGNAL(viewModeChanged())); // relay signal
connect(m_view, SIGNAL(redirection(KUrl, KUrl)),
this, SLOT(slotRedirection(KUrl, KUrl)));
@ -531,7 +529,7 @@ void DolphinPart::updateNewMenu()
{
// As requested by KNewFileMenu :
m_newFileMenu->checkUpToDate();
m_newFileMenu->setViewShowsHiddenFiles(m_view->showHiddenFiles());
m_newFileMenu->setViewShowsHiddenFiles(m_view->hiddenFilesShown());
// And set the files that the menu apply on :
m_newFileMenu->setPopupFiles(url());
}
@ -548,7 +546,7 @@ void DolphinPart::updateProgress(int percent)
void DolphinPart::createDirectory()
{
m_newFileMenu->setViewShowsHiddenFiles(m_view->showHiddenFiles());
m_newFileMenu->setViewShowsHiddenFiles(m_view->hiddenFilesShown());
m_newFileMenu->setPopupFiles(url());
m_newFileMenu->createDirectory();
}

View file

@ -271,7 +271,7 @@ Name[te]=వివరాలు
Name[tg]=Тафсилотҳо
Name[th]=
Name[tr]=Ayrıntılar
Name[ug]=تەپسىلاتى
Name[ug]=تەپسىلاتلار
Name[uk]=Подробиці
Name[uz]=Tafsilotlar
Name[uz@cyrillic]=Тафсилотлар

View file

@ -90,8 +90,8 @@
<Action name="go_forward" />
<Separator name="separator_1" />
<Action name="icons" />
<Action name="compact" />
<Action name="details" />
<Action name="columns" />
<Separator name="separator_0" />
<Action name="edit_find"/>
<Action name="show_preview" />
@ -104,8 +104,8 @@
<Action priority="0" name="go_home"/>
<Action priority="0" name="stop"/>
<Action priority="0" name="icons"/>
<Action priority="0" name="compact"/>
<Action priority="0" name="details"/>
<Action priority="0" name="columns"/>
<Action priority="0" name="view_zoom_in"/>
<Action priority="0" name="view_zoom_out"/>
<Action priority="0" name="edit_cut"/>

View file

@ -51,12 +51,6 @@
#include "search/dolphinsearchbox.h"
#include "settings/dolphinsettings.h"
#include "statusbar/dolphinstatusbar.h"
#include "views/dolphincolumnview.h"
#include "views/dolphindetailsview.h"
#include "views/draganddrophelper.h"
#include "views/dolphiniconsview.h"
#include "views/dolphinmodel.h"
#include "views/dolphinviewcontroller.h"
#include "views/viewmodecontroller.h"
#include "views/viewproperties.h"
@ -115,11 +109,12 @@ DolphinViewContainer::DolphinViewContainer(const KUrl& url, QWidget* parent) :
connect(m_view, SIGNAL(urlIsFileError(const KUrl&)), this, SLOT(openFile(const KUrl&)));
connect(m_view, SIGNAL(selectionChanged(const KFileItemList&)), this, SLOT(delayedStatusBarUpdate()));
connect(m_view, SIGNAL(operationCompletedMessage(const QString&)), this, SLOT(showOperationCompletedMessage(const QString&)));
connect(m_view, SIGNAL(urlAboutToBeChanged(const KUrl&)), this, SLOT(slotViewUrlAboutToBeChanged(const KUrl&)));
connect(m_urlNavigator, SIGNAL(urlAboutToBeChanged(const KUrl&)),
this, SLOT(slotUrlNavigatorLocationAboutToBeChanged(const KUrl&)));
connect(m_urlNavigator, SIGNAL(urlChanged(const KUrl&)),
this, SLOT(slotUrlNavigatorLocationChanged(const KUrl&)));
connect(m_urlNavigator, SIGNAL(urlAboutToBeChanged(const KUrl&)),
this, SLOT(saveViewState()));
connect(m_urlNavigator, SIGNAL(historyChanged()),
this, SLOT(slotHistoryChanged()));
@ -378,6 +373,50 @@ void DolphinViewContainer::slotFinishedPathLoading()
}
}
void DolphinViewContainer::slotItemTriggered(const KFileItem& item)
{
KUrl url = item.targetUrl();
if (item.isDir()) {
m_view->setUrl(url);
return;
}
const GeneralSettings* settings = DolphinSettings::instance().generalSettings();
const bool browseThroughArchives = settings->browseThroughArchives();
if (browseThroughArchives && item.isFile() && url.isLocalFile()) {
// Generic mechanism for redirecting to tar:/<path>/ when clicking on a tar file,
// zip:/<path>/ when clicking on a zip file, etc.
// The .protocol file specifies the mimetype that the kioslave handles.
// Note that we don't use mimetype inheritance since we don't want to
// open OpenDocument files as zip folders...
const QString protocol = KProtocolManager::protocolForArchiveMimetype(item.mimetype());
if (!protocol.isEmpty()) {
url.setProtocol(protocol);
m_view->setUrl(url);
return;
}
}
if (item.mimetype() == QLatin1String("application/x-desktop")) {
// Redirect to the URL in Type=Link desktop files
KDesktopFile desktopFile(url.toLocalFile());
if (desktopFile.hasLinkType()) {
url = desktopFile.readUrl();
m_view->setUrl(url);
return;
}
}
item.run();
}
void DolphinViewContainer::openFile(const KUrl& url)
{
const KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
slotItemTriggered(item);
}
void DolphinViewContainer::showItemInfo(const KFileItem& item)
{
if (item.isNull()) {
@ -427,20 +466,36 @@ void DolphinViewContainer::activate()
setActive(true);
}
void DolphinViewContainer::saveViewState()
void DolphinViewContainer::slotViewUrlAboutToBeChanged(const KUrl& url)
{
QByteArray locationState;
QDataStream stream(&locationState, QIODevice::WriteOnly);
m_view->saveState(stream);
m_urlNavigator->saveLocationState(locationState);
// URL changes of the view can happen in two ways:
// 1. The URL navigator gets changed and will trigger the view to update its URL
// 2. The URL of the view gets changed and will trigger the URL navigator to update
// its URL (e.g. by clicking on an item)
// In this scope the view-state may only get saved in case 2:
if (url != m_urlNavigator->locationUrl()) {
saveViewState();
}
}
void DolphinViewContainer::slotUrlNavigatorLocationAboutToBeChanged(const KUrl& url)
{
// URL changes of the view can happen in two ways:
// 1. The URL navigator gets changed and will trigger the view to update its URL
// 2. The URL of the view gets changed and will trigger the URL navigator to update
// its URL (e.g. by clicking on an item)
// In this scope the view-state may only get saved in case 1:
if (url != m_view->url()) {
saveViewState();
}
}
void DolphinViewContainer::slotUrlNavigatorLocationChanged(const KUrl& url)
{
if (KProtocolManager::supportsListing(url)) {
setSearchModeEnabled(isSearchUrl(url));
m_view->setUrl(url);
if (isActive() && !isSearchUrl(url)) {
// When an URL has been entered, the view should get the focus.
// The focus must be requested asynchronously, as changing the URL might create
@ -476,7 +531,9 @@ void DolphinViewContainer::slotUrlNavigatorLocationChanged(const KUrl& url)
void DolphinViewContainer::dropUrls(const KUrl& destination, QDropEvent* event)
{
DragAndDropHelper::instance().dropUrls(KFileItem(), destination, event, this);
Q_UNUSED(destination);
Q_UNUSED(event);
//DragAndDropHelper::instance().dropUrls(KFileItem(), destination, event, this);
}
void DolphinViewContainer::redirect(const KUrl& oldUrl, const KUrl& newUrl)
@ -510,7 +567,6 @@ void DolphinViewContainer::saveUrlCompletionMode(KGlobalSettings::Completion com
void DolphinViewContainer::slotHistoryChanged()
{
QByteArray locationState = m_urlNavigator->locationState();
if (!locationState.isEmpty()) {
QDataStream stream(&locationState, QIODevice::ReadOnly);
m_view->restoreState(stream);
@ -543,48 +599,12 @@ bool DolphinViewContainer::isSearchUrl(const KUrl& url) const
return protocol.contains("search") || (protocol == QLatin1String("nepomuk"));
}
void DolphinViewContainer::slotItemTriggered(const KFileItem& item)
void DolphinViewContainer::saveViewState()
{
KUrl url = item.targetUrl();
if (item.isDir()) {
m_view->setUrl(url);
return;
}
const GeneralSettings* settings = DolphinSettings::instance().generalSettings();
const bool browseThroughArchives = settings->browseThroughArchives();
if (browseThroughArchives && item.isFile() && url.isLocalFile()) {
// Generic mechanism for redirecting to tar:/<path>/ when clicking on a tar file,
// zip:/<path>/ when clicking on a zip file, etc.
// The .protocol file specifies the mimetype that the kioslave handles.
// Note that we don't use mimetype inheritance since we don't want to
// open OpenDocument files as zip folders...
const QString protocol = KProtocolManager::protocolForArchiveMimetype(item.mimetype());
if (!protocol.isEmpty()) {
url.setProtocol(protocol);
m_view->setUrl(url);
return;
}
}
if (item.mimetype() == "application/x-desktop") {
// redirect to the url in Type=Link desktop files
KDesktopFile desktopFile(url.toLocalFile());
if (desktopFile.hasLinkType()) {
url = desktopFile.readUrl();
m_view->setUrl(url);
return;
}
}
item.run();
}
void DolphinViewContainer::openFile(const KUrl& url)
{
const KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url);
slotItemTriggered(item);
QByteArray locationState;
QDataStream stream(&locationState, QIODevice::WriteOnly);
m_view->saveState(stream);
m_urlNavigator->saveLocationState(locationState);
}
#include "dolphinviewcontainer.moc"

View file

@ -42,7 +42,7 @@ class DolphinStatusBar;
* @short Represents a view for the directory content
* including the navigation bar, filter bar and status bar.
*
* View modes for icons, details and columns are supported. Currently
* View modes for icons, compact and details are supported. Currently
* Dolphin allows to have up to two views inside the main window.
*
* @see DolphinView
@ -212,10 +212,16 @@ private slots:
void activate();
/**
* Saves the state of the current view: contents position,
* root URL, ...
* Is invoked if the signal urlAboutToBeChanged() from the DolphinView
* is emitted. Tries to save the view-state.
*/
void saveViewState();
void slotViewUrlAboutToBeChanged(const KUrl& url);
/**
* Is invoked if the signal urlAboutToBeChanged() from the URL navigator
* is emitted. Tries to save the view-state.
*/
void slotUrlNavigatorLocationAboutToBeChanged(const KUrl& url);
/**
* Restores the current view to show \a url and assures
@ -266,6 +272,12 @@ private:
*/
bool isSearchUrl(const KUrl& url) const;
/**
* Saves the state of the current view: contents position,
* root URL, ...
*/
void saveViewState();
private:
QVBoxLayout* m_topLayout;
KUrlNavigator* m_urlNavigator;

View file

@ -0,0 +1,423 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 "kfileitemlistview.h"
#include "kitemlistgroupheader.h"
#include "kfileitemmodelrolesupdater.h"
#include "kfileitemlistwidget.h"
#include "kfileitemmodel.h"
#include <KLocale>
#include <KStringHandler>
#include <KDebug>
#include <QTextLine>
#include <QTimer>
#define KFILEITEMLISTVIEW_DEBUG
namespace {
const int ShortInterval = 50;
const int LongInterval = 300;
}
KFileItemListView::KFileItemListView(QGraphicsWidget* parent) :
KItemListView(parent),
m_itemLayout(IconsLayout),
m_modelRolesUpdater(0),
m_updateVisibleIndexRangeTimer(0),
m_updateIconSizeTimer(0),
m_minimumRolesWidths()
{
setScrollOrientation(Qt::Vertical);
setWidgetCreator(new KItemListWidgetCreator<KFileItemListWidget>());
setGroupHeaderCreator(new KItemListGroupHeaderCreator<KItemListGroupHeader>());
m_updateVisibleIndexRangeTimer = new QTimer(this);
m_updateVisibleIndexRangeTimer->setSingleShot(true);
m_updateVisibleIndexRangeTimer->setInterval(ShortInterval);
connect(m_updateVisibleIndexRangeTimer, SIGNAL(timeout()), this, SLOT(updateVisibleIndexRange()));
m_updateIconSizeTimer = new QTimer(this);
m_updateIconSizeTimer->setSingleShot(true);
m_updateIconSizeTimer->setInterval(ShortInterval);
connect(m_updateIconSizeTimer, SIGNAL(timeout()), this, SLOT(updateIconSize()));
updateMinimumRolesWidths();
}
KFileItemListView::~KFileItemListView()
{
delete widgetCreator();
delete groupHeaderCreator();
delete m_modelRolesUpdater;
m_modelRolesUpdater = 0;
}
void KFileItemListView::setPreviewsShown(bool show)
{
if (m_modelRolesUpdater) {
m_modelRolesUpdater->setPreviewShown(show);
}
}
bool KFileItemListView::previewsShown() const
{
return m_modelRolesUpdater->isPreviewShown();
}
void KFileItemListView::setItemLayout(Layout layout)
{
if (m_itemLayout != layout) {
m_itemLayout = layout;
updateLayoutOfVisibleItems();
}
}
KFileItemListView::Layout KFileItemListView::itemLayout() const
{
return m_itemLayout;
}
QSizeF KFileItemListView::itemSizeHint(int index) const
{
const QHash<QByteArray, QVariant> values = model()->data(index);
const KItemListStyleOption& option = styleOption();
const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0);
switch (m_itemLayout) {
case IconsLayout: {
const QString text = KStringHandler::preProcessWrap(values["name"].toString());
const qreal maxWidth = itemSize().width() - 2 * option.margin;
int textLinesCount = 0;
QTextLine line;
// Calculate the number of lines required for wrapping the name
QTextOption textOption(Qt::AlignHCenter);
textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
QTextLayout layout(text, option.font);
layout.setTextOption(textOption);
layout.beginLayout();
while ((line = layout.createLine()).isValid()) {
line.setLineWidth(maxWidth);
line.naturalTextWidth();
++textLinesCount;
}
layout.endLayout();
// Add one line for each additional information
textLinesCount += additionalRolesCount;
const qreal height = textLinesCount * option.fontMetrics.height() +
option.iconSize +
option.margin * 4;
return QSizeF(itemSize().width(), height);
}
case CompactLayout: {
// For each row exactly one role is shown. Calculate the maximum required width that is necessary
// to show all roles without horizontal clipping.
qreal maximumRequiredWidth = 0.0;
QHashIterator<QByteArray, int> it(visibleRoles());
while (it.hasNext()) {
it.next();
const QByteArray& role = it.key();
const QString text = values[role].toString();
const qreal requiredWidth = option.fontMetrics.width(text);
maximumRequiredWidth = qMax(maximumRequiredWidth, requiredWidth);
}
const qreal width = option.margin * 4 + option.iconSize + maximumRequiredWidth;
const qreal height = option.margin * 2 + qMax(option.iconSize, (1 + additionalRolesCount) * option.fontMetrics.height());
return QSizeF(width, height);
}
case DetailsLayout: {
// The width will be determined dynamically by KFileItemListView::visibleRoleSizes()
const qreal height = option.margin * 2 + qMax(option.iconSize, option.fontMetrics.height());
return QSizeF(-1, height);
}
default:
Q_ASSERT(false);
break;
}
return QSize();
}
QHash<QByteArray, QSizeF> KFileItemListView::visibleRoleSizes() const
{
QElapsedTimer timer;
timer.start();
QHash<QByteArray, QSizeF> sizes;
const int itemCount = model()->count();
for (int i = 0; i < itemCount; ++i) {
QHashIterator<QByteArray, int> it(visibleRoles());
while (it.hasNext()) {
it.next();
const QByteArray& visibleRole = it.key();
QSizeF maxSize = sizes.value(visibleRole, QSizeF(0, 0));
const QSizeF itemSize = visibleRoleSizeHint(i, visibleRole);
maxSize = maxSize.expandedTo(itemSize);
sizes.insert(visibleRole, maxSize);
}
if (i > 100 && timer.elapsed() > 200) {
// When having several thousands of items calculating the sizes can get
// very expensive. We accept a possibly too small role-size in favour
// of having no blocking user interface.
#ifdef KFILEITEMLISTVIEW_DEBUG
kDebug() << "Timer exceeded, stopped after" << i << "items";
#endif
break;
}
}
#ifdef KFILEITEMLISTVIEW_DEBUG
kDebug() << "[TIME] Calculated dynamic item size for " << itemCount << "items:" << timer.elapsed();
#endif
return sizes;
}
void KFileItemListView::initializeItemListWidget(KItemListWidget* item)
{
KFileItemListWidget* fileItemListWidget = static_cast<KFileItemListWidget*>(item);
switch (m_itemLayout) {
case IconsLayout: fileItemListWidget->setLayout(KFileItemListWidget::IconsLayout); break;
case CompactLayout: fileItemListWidget->setLayout(KFileItemListWidget::CompactLayout); break;
case DetailsLayout: fileItemListWidget->setLayout(KFileItemListWidget::DetailsLayout); break;
default: Q_ASSERT(false); break;
}
}
void KFileItemListView::onModelChanged(KItemModelBase* current, KItemModelBase* previous)
{
Q_UNUSED(previous);
Q_ASSERT(qobject_cast<KFileItemModel*>(current));
if (m_modelRolesUpdater) {
delete m_modelRolesUpdater;
}
m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast<KFileItemModel*>(current), this);
const int size = styleOption().iconSize;
m_modelRolesUpdater->setIconSize(QSize(size, size));
}
void KFileItemListView::onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous)
{
Q_UNUSED(current);
Q_UNUSED(previous);
updateLayoutOfVisibleItems();
}
void KFileItemListView::onItemSizeChanged(const QSizeF& current, const QSizeF& previous)
{
Q_UNUSED(current);
Q_UNUSED(previous);
triggerVisibleIndexRangeUpdate();
}
void KFileItemListView::onOffsetChanged(qreal current, qreal previous)
{
Q_UNUSED(current);
Q_UNUSED(previous);
triggerVisibleIndexRangeUpdate();
}
void KFileItemListView::onVisibleRolesChanged(const QHash<QByteArray, int>& current, const QHash<QByteArray, int>& previous)
{
Q_UNUSED(previous);
Q_ASSERT(qobject_cast<KFileItemModel*>(model()));
KFileItemModel* fileItemModel = static_cast<KFileItemModel*>(model());
// KFileItemModel does not distinct between "visible" and "invisible" roles.
// Add all roles that are mandatory for having a working KFileItemListView:
QSet<QByteArray> keys = current.keys().toSet();
QSet<QByteArray> roles = keys;
roles.insert("iconPixmap");
roles.insert("iconName");
roles.insert("name"); // TODO: just don't allow to disable it
roles.insert("isDir");
if (m_itemLayout == DetailsLayout) {
roles.insert("isExpanded");
roles.insert("expansionLevel");
}
fileItemModel->setRoles(roles);
m_modelRolesUpdater->setRoles(keys);
}
void KFileItemListView::onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous)
{
Q_UNUSED(current);
Q_UNUSED(previous);
triggerIconSizeUpdate();
}
void KFileItemListView::onTransactionBegin()
{
m_modelRolesUpdater->setPaused(true);
}
void KFileItemListView::onTransactionEnd()
{
// Only unpause the model-roles-updater if no timer is active. If one
// timer is still active the model-roles-updater will be unpaused later as
// soon as the timer has been exceeded.
const bool timerActive = m_updateVisibleIndexRangeTimer->isActive() ||
m_updateIconSizeTimer->isActive();
if (!timerActive) {
m_modelRolesUpdater->setPaused(false);
}
}
void KFileItemListView::resizeEvent(QGraphicsSceneResizeEvent* event)
{
KItemListView::resizeEvent(event);
triggerVisibleIndexRangeUpdate();
}
void KFileItemListView::slotItemsRemoved(const KItemRangeList& itemRanges)
{
KItemListView::slotItemsRemoved(itemRanges);
updateTimersInterval();
}
void KFileItemListView::triggerVisibleIndexRangeUpdate()
{
m_modelRolesUpdater->setPaused(true);
m_updateVisibleIndexRangeTimer->start();
}
void KFileItemListView::updateVisibleIndexRange()
{
if (!m_modelRolesUpdater) {
return;
}
const int index = firstVisibleIndex();
const int count = lastVisibleIndex() - index + 1;
m_modelRolesUpdater->setVisibleIndexRange(index, count);
if (m_updateIconSizeTimer->isActive()) {
// If the icon-size update is pending do an immediate update
// of the icon-size before unpausing m_modelRolesUpdater. This prevents
// an unnecessary expensive recreation of all previews afterwards.
m_updateIconSizeTimer->stop();
const KItemListStyleOption& option = styleOption();
m_modelRolesUpdater->setIconSize(QSize(option.iconSize, option.iconSize));
}
m_modelRolesUpdater->setPaused(isTransactionActive());
updateTimersInterval();
}
void KFileItemListView::triggerIconSizeUpdate()
{
m_modelRolesUpdater->setPaused(true);
m_updateIconSizeTimer->start();
}
void KFileItemListView::updateIconSize()
{
if (!m_modelRolesUpdater) {
return;
}
const KItemListStyleOption& option = styleOption();
m_modelRolesUpdater->setIconSize(QSize(option.iconSize, option.iconSize));
if (m_updateVisibleIndexRangeTimer->isActive()) {
// If the visibility-index-range update is pending do an immediate update
// of the range before unpausing m_modelRolesUpdater. This prevents
// an unnecessary expensive recreation of all previews afterwards.
m_updateVisibleIndexRangeTimer->stop();
const int index = firstVisibleIndex();
const int count = lastVisibleIndex() - index + 1;
m_modelRolesUpdater->setVisibleIndexRange(index, count);
}
m_modelRolesUpdater->setPaused(isTransactionActive());
updateTimersInterval();
}
QSizeF KFileItemListView::visibleRoleSizeHint(int index, const QByteArray& role) const
{
const KItemListStyleOption& option = styleOption();
qreal width = m_minimumRolesWidths.value(role, 0);
const qreal height = option.margin * 2 + option.fontMetrics.height();
const QVariant value = model()->data(index).value(role);
const QString text = value.toString();
if (!text.isEmpty()) {
width = qMax(width, qreal(option.margin * 2 + option.fontMetrics.width(text)));
}
return QSizeF(width, height);
}
void KFileItemListView::updateLayoutOfVisibleItems()
{
foreach (KItemListWidget* widget, visibleItemListWidgets()) {
initializeItemListWidget(widget);
}
triggerVisibleIndexRangeUpdate();
}
void KFileItemListView::updateTimersInterval()
{
if (!model()) {
return;
}
// The ShortInterval is used for cases like switching the directory: If the
// model is empty and filled later the creation of the previews should be done
// as soon as possible. The LongInterval is used when the model already contains
// items and assures that operations like zooming don't result in too many temporary
// recreations of the previews.
const int interval = (model()->count() <= 0) ? ShortInterval : LongInterval;
m_updateVisibleIndexRangeTimer->setInterval(interval);
m_updateIconSizeTimer->setInterval(interval);
}
void KFileItemListView::updateMinimumRolesWidths()
{
m_minimumRolesWidths.clear();
const KItemListStyleOption& option = styleOption();
const QString sizeText = QLatin1String("888888") + i18nc("@item:intable", "items");
m_minimumRolesWidths.insert("size", option.fontMetrics.width(sizeText));
}
#include "kfileitemlistview.moc"

View file

@ -0,0 +1,97 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
***************************************************************************/
#ifndef KFILEITEMLISTVIEW_H
#define KFILEITEMLISTVIEW_H
#include <libdolphin_export.h>
#include <kitemviews/kitemlistview.h>
class KFileItemModelRolesUpdater;
class QTimer;
class LIBDOLPHINPRIVATE_EXPORT KFileItemListView : public KItemListView
{
Q_OBJECT
public:
enum Layout
{
IconsLayout,
CompactLayout,
DetailsLayout
};
KFileItemListView(QGraphicsWidget* parent = 0);
virtual ~KFileItemListView();
void setPreviewsShown(bool show);
bool previewsShown() const;
void setItemLayout(Layout layout);
Layout itemLayout() const;
virtual QSizeF itemSizeHint(int index) const;
virtual QHash<QByteArray, QSizeF> visibleRoleSizes() const;
protected:
virtual void initializeItemListWidget(KItemListWidget* item);
virtual void onModelChanged(KItemModelBase* current, KItemModelBase* previous);
virtual void onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous);
virtual void onItemSizeChanged(const QSizeF& current, const QSizeF& previous);
virtual void onOffsetChanged(qreal current, qreal previous);
virtual void onVisibleRolesChanged(const QHash<QByteArray, int>& current, const QHash<QByteArray, int>& previous);
virtual void onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous);
virtual void onTransactionBegin();
virtual void onTransactionEnd();
virtual void resizeEvent(QGraphicsSceneResizeEvent* event);
protected slots:
virtual void slotItemsRemoved(const KItemRangeList& itemRanges);
private slots:
void triggerVisibleIndexRangeUpdate();
void updateVisibleIndexRange();
void triggerIconSizeUpdate();
void updateIconSize();
private:
QSizeF visibleRoleSizeHint(int index, const QByteArray& role) const;
void updateLayoutOfVisibleItems();
void updateTimersInterval();
void updateMinimumRolesWidths();
private:
Layout m_itemLayout;
KFileItemModelRolesUpdater* m_modelRolesUpdater;
QTimer* m_updateVisibleIndexRangeTimer;
QTimer* m_updateIconSizeTimer;
// Cache for calculating visibleRoleSizes() in a fast way
QHash<QByteArray, int> m_minimumRolesWidths;
friend class KFileItemListViewTest; // For unit testing
};
#endif

View file

@ -0,0 +1,728 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 "kfileitemlistwidget.h"
#include "kfileitemmodel.h"
#include "kitemlistview.h"
#include "kpixmapmodifier_p.h"
#include <KIcon>
#include <KIconEffect>
#include <KIconLoader>
#include <KLocale>
#include <KStringHandler>
#include <KDebug>
#include <QFontMetricsF>
#include <QGraphicsSceneResizeEvent>
#include <QPainter>
#include <QTextLayout>
#include <QTextLine>
//#define KFILEITEMLISTWIDGET_DEBUG
KFileItemListWidget::KFileItemListWidget(QGraphicsItem* parent) :
KItemListWidget(parent),
m_isDir(false),
m_dirtyLayout(true),
m_dirtyContent(true),
m_dirtyContentRoles(),
m_layout(IconsLayout),
m_pixmapPos(),
m_pixmap(),
m_scaledPixmapSize(),
m_hoverPixmapRect(),
m_hoverPixmap(),
m_textPos(),
m_text(),
m_textsBoundingRect(),
m_sortedVisibleRoles(),
m_expansionArea(),
m_additionalInfoTextColor()
{
for (int i = 0; i < TextIdCount; ++i) {
m_text[i].setTextFormat(Qt::PlainText);
m_text[i].setPerformanceHint(QStaticText::AggressiveCaching);
}
}
KFileItemListWidget::~KFileItemListWidget()
{
}
void KFileItemListWidget::setLayout(Layout layout)
{
if (m_layout != layout) {
m_layout = layout;
m_dirtyLayout = true;
update();
}
}
KFileItemListWidget::Layout KFileItemListWidget::layout() const
{
return m_layout;
}
void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
KItemListWidget::paint(painter, option, widget);
painter->setRenderHint(QPainter::Antialiasing);
if (m_dirtyContent || m_dirtyLayout) {
const_cast<KFileItemListWidget*>(this)->updateCache();
}
if (m_isDir && !m_expansionArea.isEmpty()) {
QStyleOption arrowOption;
arrowOption.rect = m_expansionArea.toRect();
const QStyle::PrimitiveElement arrow = data()["isExpanded"].toBool()
? QStyle::PE_IndicatorArrowDown : QStyle::PE_IndicatorArrowRight;
style()->drawPrimitive(arrow, &arrowOption, painter);
}
const bool isHovered = (hoverOpacity() > 0.0);
const KItemListStyleOption& itemListStyleOption = styleOption();
if (isHovered) {
// Blend the unhovered and hovered pixmap if the hovering
// animation is ongoing
if (hoverOpacity() < 1.0) {
drawPixmap(painter, m_pixmap);
}
const qreal opacity = painter->opacity();
painter->setOpacity(hoverOpacity() * opacity);
drawPixmap(painter, m_hoverPixmap);
// Draw the hover background for the text
QRectF textsBoundingRect = m_textsBoundingRect;
const qreal marginDiff = itemListStyleOption.margin / 2;
textsBoundingRect.adjust(marginDiff, marginDiff, -marginDiff, -marginDiff);
painter->setOpacity(hoverOpacity() * opacity * 0.1);
painter->setPen(Qt::NoPen);
painter->setBrush(itemListStyleOption.palette.text());
painter->drawRoundedRect(textsBoundingRect, 4, 4);
painter->setOpacity(opacity);
} else {
drawPixmap(painter, m_pixmap);
}
painter->setFont(itemListStyleOption.font);
painter->setPen(itemListStyleOption.palette.text().color());
painter->drawStaticText(m_textPos[Name], m_text[Name]);
painter->setPen(m_additionalInfoTextColor);
for (int i = Name + 1; i < TextIdCount; ++i) {
painter->drawStaticText(m_textPos[i], m_text[i]);
}
#ifdef KFILEITEMLISTWIDGET_DEBUG
painter->setPen(Qt::red);
painter->setBrush(Qt::NoBrush);
painter->drawText(QPointF(0, itemListStyleOption.fontMetrics.height()), QString::number(index()));
painter->drawRect(rect());
#endif
}
bool KFileItemListWidget::contains(const QPointF& point) const
{
return KItemListWidget::contains(point) || m_textsBoundingRect.contains(point);
}
QRectF KFileItemListWidget::hoverBoundingRect() const
{
QRectF bounds = m_hoverPixmapRect;
const qreal margin = styleOption().margin;
bounds.adjust(-margin, -margin, margin, margin);
return bounds;
}
QRectF KFileItemListWidget::expansionToggleRect() const
{
return m_isDir ? m_expansionArea : QRectF();
}
void KFileItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& current,
const QSet<QByteArray>& roles)
{
KItemListWidget::dataChanged(current, roles);
m_dirtyContent = true;
QSet<QByteArray> dirtyRoles;
if (roles.isEmpty()) {
dirtyRoles = visibleRoles().keys().toSet();
dirtyRoles.insert("iconPixmap");
dirtyRoles.insert("iconName");
} else {
dirtyRoles = roles;
}
QSetIterator<QByteArray> it(dirtyRoles);
while (it.hasNext()) {
const QByteArray& role = it.next();
m_dirtyContentRoles.insert(role);
}
}
void KFileItemListWidget::visibleRolesChanged(const QHash<QByteArray, int>& current,
const QHash<QByteArray, int>& previous)
{
KItemListWidget::visibleRolesChanged(current, previous);
m_dirtyLayout = true;
// Cache the roles sorted into m_sortedVisibleRoles:
const int visibleRolesCount = current.count();
m_sortedVisibleRoles.clear();
m_sortedVisibleRoles.reserve(visibleRolesCount);
for (int i = 0; i < visibleRolesCount; ++i) {
m_sortedVisibleRoles.append(QByteArray());
}
QHashIterator<QByteArray, int> it(current);
while (it.hasNext()) {
it.next();
const int index = it.value();
if (index < 0 || index >= visibleRolesCount || !m_sortedVisibleRoles.at(index).isEmpty()) {
kWarning() << "The visible roles have an invalid sort order.";
break;
}
const QByteArray& role = it.key();
m_sortedVisibleRoles[index] = role;
}
}
void KFileItemListWidget::visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current,
const QHash<QByteArray, QSizeF>& previous)
{
KItemListWidget::visibleRolesSizesChanged(current, previous);
m_dirtyLayout = true;
}
void KFileItemListWidget::styleOptionChanged(const KItemListStyleOption& current,
const KItemListStyleOption& previous)
{
KItemListWidget::styleOptionChanged(current, previous);
// For the color of the additional info the inactive text color
// is not used as this might lead to unreadable text for some color schemes. Instead
// the text color is slightly mixed with the background color.
const QColor c1 = current.palette.text().color();
const QColor c2 = current.palette.background().color();
const int p1 = 70;
const int p2 = 100 - p1;
m_additionalInfoTextColor = QColor((c1.red() * p1 + c2.red() * p2) / 100,
(c1.green() * p1 + c2.green() * p2) / 100,
(c1.blue() * p1 + c2.blue() * p2) / 100);
m_dirtyLayout = true;
}
void KFileItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event)
{
KItemListWidget::resizeEvent(event);
m_dirtyLayout = true;
}
void KFileItemListWidget::updateCache()
{
if (index() < 0) {
return;
}
m_isDir = data()["isDir"].toBool();
updateExpansionArea();
updateTextsCache();
updatePixmapCache();
m_dirtyLayout = false;
m_dirtyContent = false;
m_dirtyContentRoles.clear();
}
void KFileItemListWidget::updateExpansionArea()
{
if (m_layout == DetailsLayout) {
const QHash<QByteArray, QVariant> values = data();
Q_ASSERT(values.contains("expansionLevel"));
const KItemListStyleOption& option = styleOption();
const int expansionLevel = values.value("expansionLevel", 0).toInt();
const qreal widgetHeight = size().height();
const qreal expansionLevelSize = KIconLoader::SizeSmall;
const qreal x = option.margin + expansionLevel * widgetHeight;
const qreal y = (widgetHeight - expansionLevelSize) / 2;
m_expansionArea = QRectF(x, y, expansionLevelSize, expansionLevelSize);
} else {
m_expansionArea = QRectF();
}
}
void KFileItemListWidget::updatePixmapCache()
{
// Precondition: Requires already updated m_textPos values to calculate
// the remaining height when the alignment is vertical.
const bool iconOnTop = (m_layout == IconsLayout);
const KItemListStyleOption& option = styleOption();
const int iconHeight = option.iconSize;
const QHash<QByteArray, QVariant> values = data();
const QSizeF widgetSize = size();
int scaledIconHeight = 0;
if (iconOnTop) {
scaledIconHeight = static_cast<int>(m_textPos[Name].y() - 3 * option.margin);
} else {
const int textRowsCount = (m_layout == CompactLayout) ? visibleRoles().count() : 1;
const qreal requiredTextHeight = textRowsCount * option.fontMetrics.height();
scaledIconHeight = (requiredTextHeight < iconHeight) ? widgetSize.height() - 2 * option.margin : iconHeight;
}
bool updatePixmap = (iconHeight != m_pixmap.height());
if (!updatePixmap && m_dirtyContent) {
updatePixmap = m_dirtyContentRoles.isEmpty()
|| m_dirtyContentRoles.contains("iconPixmap")
|| m_dirtyContentRoles.contains("iconName");
}
if (updatePixmap) {
m_pixmap = values["iconPixmap"].value<QPixmap>();
if (m_pixmap.isNull()) {
// Use the icon that fits to the MIME-type
QString iconName = values["iconName"].toString();
if (iconName.isEmpty()) {
// The icon-name has not been not resolved by KFileItemModelRolesUpdater,
// use a generic icon as fallback
iconName = QLatin1String("unknown");
}
m_pixmap = pixmapForIcon(iconName, iconHeight);
m_hoverPixmapRect.setSize(m_pixmap.size());
} else if (m_pixmap.size() != QSize(iconHeight, iconHeight)) {
// A custom pixmap has been applied. Assure that the pixmap
// is scaled to the available size.
const bool scale = m_pixmap.width() > iconHeight || m_pixmap.height() > iconHeight ||
(m_pixmap.width() < iconHeight && m_pixmap.height() < iconHeight);
if (scale) {
KPixmapModifier::scale(m_pixmap, QSize(iconHeight, iconHeight));
}
m_hoverPixmapRect.setSize(m_pixmap.size());
// To simplify the handling of scaling the original pixmap
// will be embedded into a square pixmap.
QPixmap squarePixmap(iconHeight, iconHeight);
squarePixmap.fill(Qt::transparent);
QPainter painter(&squarePixmap);
if (iconOnTop) {
const int x = (iconHeight - m_pixmap.width()) / 2; // Center horizontally
const int y = iconHeight - m_pixmap.height(); // Align on bottom
painter.drawPixmap(x, y, m_pixmap);
} else {
const int x = iconHeight - m_pixmap.width(); // Align right
const int y = (iconHeight - m_pixmap.height()) / 2; // Center vertically
painter.drawPixmap(x, y, m_pixmap);
}
m_pixmap = squarePixmap;
} else {
m_hoverPixmapRect.setSize(m_pixmap.size());
}
Q_ASSERT(m_pixmap.height() == iconHeight);
}
m_scaledPixmapSize = QSize(scaledIconHeight, scaledIconHeight);
if (iconOnTop) {
m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2);
} else {
m_pixmapPos.setX(m_textPos[Name].x() - 2 * option.margin - scaledIconHeight);
}
m_pixmapPos.setY(option.margin);
// Center the hover rectangle horizontally and align it on bottom
const qreal x = m_pixmapPos.x() + (m_scaledPixmapSize.width() - m_hoverPixmapRect.width()) / 2.0;
const qreal y = m_pixmapPos.y() + m_scaledPixmapSize.height() - m_hoverPixmapRect.height();
m_hoverPixmapRect.moveTopLeft(QPointF(x, y));
// Prepare the pixmap that is used when the item gets hovered
if (option.state & QStyle::State_MouseOver) {
m_hoverPixmap = m_pixmap;
KIconEffect* effect = KIconLoader::global()->iconEffect();
// In the KIconLoader terminology, active = hover.
if (effect->hasEffect(KIconLoader::Desktop, KIconLoader::ActiveState)) {
m_hoverPixmap = effect->apply(m_pixmap, KIconLoader::Desktop, KIconLoader::ActiveState);
} else {
m_hoverPixmap = m_pixmap;
}
} else if (hoverOpacity() <= 0.0) {
// No hover animation is ongoing. Clear m_hoverPixmap to save memory.
m_hoverPixmap = QPixmap();
}
}
void KFileItemListWidget::updateTextsCache()
{
QTextOption textOption;
switch (m_layout) {
case IconsLayout:
textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
textOption.setAlignment(Qt::AlignHCenter);
break;
case CompactLayout:
case DetailsLayout:
textOption.setAlignment(Qt::AlignLeft);
textOption.setWrapMode(QTextOption::NoWrap);
break;
default:
Q_ASSERT(false);
break;
}
for (int i = 0; i < TextIdCount; ++i) {
m_text[i].setText(QString());
m_text[i].setTextOption(textOption);
}
switch (m_layout) {
case IconsLayout: updateIconsLayoutTextCache(); break;
case CompactLayout: updateCompactLayoutTextCache(); break;
case DetailsLayout: updateDetailsLayoutTextCache(); break;
default: Q_ASSERT(false); break;
}
}
void KFileItemListWidget::updateIconsLayoutTextCache()
{
// +------+
// | Icon |
// +------+
//
// Name role that
// might get wrapped above
// several lines.
// Additional role 1
// Additional role 2
const QHash<QByteArray, QVariant> values = data();
const KItemListStyleOption& option = styleOption();
const qreal maxWidth = size().width() - 2 * option.margin;
const qreal widgetHeight = size().height();
const qreal fontHeight = option.fontMetrics.height();
// Initialize properties for the "name" role. It will be used as anchor
// for initializing the position of the other roles.
m_text[Name].setText(KStringHandler::preProcessWrap(values["name"].toString()));
// Calculate the number of lines required for the name and the required width
int textLinesCountForName = 0;
qreal requiredWidthForName = 0;
QTextLine line;
QTextLayout layout(m_text[Name].text(), option.font);
layout.setTextOption(m_text[Name].textOption());
layout.beginLayout();
while ((line = layout.createLine()).isValid()) {
line.setLineWidth(maxWidth);
requiredWidthForName = qMax(requiredWidthForName, line.naturalTextWidth());
++textLinesCountForName;
}
layout.endLayout();
// Use one line for each additional information
int textLinesCount = textLinesCountForName;
const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0);
textLinesCount += additionalRolesCount;
m_text[Name].setTextWidth(maxWidth);
m_textPos[Name] = QPointF(option.margin, widgetHeight - textLinesCount * fontHeight - option.margin);
m_textsBoundingRect = QRectF(option.margin + (maxWidth - requiredWidthForName) / 2,
m_textPos[Name].y(),
requiredWidthForName,
m_text[Name].size().height());
// Calculate the position for each additional information
qreal y = m_textPos[Name].y() + textLinesCountForName * fontHeight;
foreach (const QByteArray& role, m_sortedVisibleRoles) {
const TextId textId = roleTextId(role);
if (textId == Name) {
continue;
}
const QString text = roleText(textId, values[role]);
m_text[textId].setText(text);
qreal requiredWidth = 0;
QTextLayout layout(text, option.font);
layout.setTextOption(m_text[textId].textOption());
layout.beginLayout();
QTextLine textLine = layout.createLine();
if (textLine.isValid()) {
textLine.setLineWidth(maxWidth);
requiredWidth = textLine.naturalTextWidth();
if (textLine.textLength() < text.length()) {
// TODO: QFontMetrics::elidedText() works different regarding the given width
// in comparison to QTextLine::setLineWidth(). It might happen that the text does
// not get elided although it does not fit into the given width. As workaround
// the margin is substracted.
const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth - option.margin);
m_text[textId].setText(elidedText);
}
}
layout.endLayout();
m_textPos[textId] = QPointF(option.margin, y);
m_text[textId].setTextWidth(maxWidth);
const QRectF textBoundingRect(option.margin + (maxWidth - requiredWidth) / 2, y, requiredWidth, fontHeight);
m_textsBoundingRect |= textBoundingRect;
y += fontHeight;
}
// Add a margin to the text bounding rectangle
const qreal margin = option.margin;
m_textsBoundingRect.adjust(-margin, -margin, margin, margin);
}
void KFileItemListWidget::updateCompactLayoutTextCache()
{
// +------+ Name role
// | Icon | Additional role 1
// +------+ Additional role 2
const QHash<QByteArray, QVariant> values = data();
const KItemListStyleOption& option = styleOption();
const qreal widgetHeight = size().height();
const qreal fontHeight = option.fontMetrics.height();
const qreal textLinesHeight = qMax(visibleRoles().count(), 1) * fontHeight;
const int scaledIconSize = (textLinesHeight < option.iconSize) ? widgetHeight - 2 * option.margin : option.iconSize;
qreal maximumRequiredTextWidth = 0;
const qreal x = option.margin * 3 + scaledIconSize;
qreal y = (widgetHeight - textLinesHeight) / 2;
const qreal maxWidth = size().width() - x - option.margin;
foreach (const QByteArray& role, m_sortedVisibleRoles) {
const TextId textId = roleTextId(role);
const QString text = roleText(textId, values[role]);
m_text[textId].setText(text);
qreal requiredWidth = option.fontMetrics.width(text);
if (requiredWidth > maxWidth) {
requiredWidth = maxWidth;
const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth);
m_text[textId].setText(elidedText);
}
m_textPos[textId] = QPointF(x, y);
m_text[textId].setTextWidth(maxWidth);
maximumRequiredTextWidth = qMax(maximumRequiredTextWidth, requiredWidth);
y += fontHeight;
}
m_textsBoundingRect = QRectF(x - option.margin, 0, maximumRequiredTextWidth + 2 * option.margin, widgetHeight);
}
void KFileItemListWidget::updateDetailsLayoutTextCache()
{
// Precondition: Requires already updated m_expansionArea
// to determine the left position.
// +------+
// | Icon | Name role Additional role 1 Additional role 2
// +------+
m_textsBoundingRect = QRectF();
const KItemListStyleOption& option = styleOption();
const QHash<QByteArray, QVariant> values = data();
const qreal widgetHeight = size().height();
const int scaledIconSize = widgetHeight - 2 * option.margin;
const int fontHeight = option.fontMetrics.height();
qreal x = m_expansionArea.right() + option.margin * 3 + scaledIconSize;
const qreal y = qMax(qreal(option.margin), (widgetHeight - fontHeight) / 2);
foreach (const QByteArray& role, m_sortedVisibleRoles) {
const TextId textId = roleTextId(role);
const QString text = roleText(textId, values[role]);
m_text[textId].setText(text);
const qreal requiredWidth = option.fontMetrics.width(text);
m_textPos[textId] = QPointF(x, y);
const qreal columnWidth = visibleRolesSizes().value(role, QSizeF(0, 0)).width();
x += columnWidth;
switch (textId) {
case Name: {
m_textsBoundingRect = QRectF(m_textPos[textId].x() - option.margin, 0,
requiredWidth + 2 * option.margin, size().height());
// The column after the name should always be aligned on the same x-position independent
// from the expansion-level shown in the name column
x -= m_expansionArea.right();
break;
}
case Size:
// The values for the size should be right aligned
m_textPos[textId].rx() += columnWidth - requiredWidth - 2 * option.margin;
break;
default:
break;
}
}
}
QString KFileItemListWidget::roleText(TextId textId, const QVariant& roleValue) const
{
QString text;
switch (textId) {
case Name:
case Permissions:
case Owner:
case Group:
case Type:
case Destination:
case Path:
text = roleValue.toString();
break;
case Size: {
if (data().value("isDir").toBool()) {
// The item represents a directory. Show the number of sub directories
// instead of the file size of the directory.
if (!roleValue.isNull()) {
const KIO::filesize_t size = roleValue.value<KIO::filesize_t>();
text = i18ncp("@item:intable", "%1 item", "%1 items", size);
}
} else {
const KIO::filesize_t size = roleValue.value<KIO::filesize_t>();
text = KIO::convertSize(size);
}
break;
}
case Date: {
const QDateTime dateTime = roleValue.toDateTime();
text = KGlobal::locale()->formatDateTime(dateTime);
break;
}
default:
Q_ASSERT(false);
break;
}
return text;
}
void KFileItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap)
{
const bool isHiddenItem = m_text[Name].text().startsWith(QLatin1Char('.'));
qreal opacity;
if (isHiddenItem) {
opacity = painter->opacity();
painter->setOpacity(opacity * 0.3);
}
if (m_scaledPixmapSize != pixmap.size()) {
QPixmap scaledPixmap = pixmap;
KPixmapModifier::scale(scaledPixmap, m_scaledPixmapSize);
painter->drawPixmap(m_pixmapPos, scaledPixmap);
#ifdef KFILEITEMLISTWIDGET_DEBUG
painter->setPen(Qt::green);
painter->drawRect(QRectF(m_pixmapPos, QSizeF(scaledPixmap.size())));
#endif
} else {
painter->drawPixmap(m_pixmapPos, pixmap);
}
if (isHiddenItem) {
painter->setOpacity(opacity);
}
}
QPixmap KFileItemListWidget::pixmapForIcon(const QString& name, int size)
{
const KIcon icon(name);
int requestedSize;
if (size <= KIconLoader::SizeSmall) {
requestedSize = KIconLoader::SizeSmall;
} else if (size <= KIconLoader::SizeSmallMedium) {
requestedSize = KIconLoader::SizeSmallMedium;
} else if (size <= KIconLoader::SizeMedium) {
requestedSize = KIconLoader::SizeMedium;
} else if (size <= KIconLoader::SizeLarge) {
requestedSize = KIconLoader::SizeLarge;
} else if (size <= KIconLoader::SizeHuge) {
requestedSize = KIconLoader::SizeHuge;
} else if (size <= KIconLoader::SizeEnormous) {
requestedSize = KIconLoader::SizeEnormous;
} else if (size <= KIconLoader::SizeEnormous * 2) {
requestedSize = KIconLoader::SizeEnormous * 2;
} else {
requestedSize = size;
}
QPixmap pixmap = icon.pixmap(requestedSize, requestedSize);
if (requestedSize != size) {
KPixmapModifier::scale(pixmap, QSize(size, size));
}
return pixmap;
}
KFileItemListWidget::TextId KFileItemListWidget::roleTextId(const QByteArray& role)
{
static QHash<QByteArray, TextId> rolesHash;
if (rolesHash.isEmpty()) {
rolesHash.insert("name", Name);
rolesHash.insert("size", Size);
rolesHash.insert("date", Date);
rolesHash.insert("permissions", Permissions);
rolesHash.insert("owner", Owner);
rolesHash.insert("group", Group);
rolesHash.insert("type", Type);
rolesHash.insert("destination", Destination);
rolesHash.insert("path", Path);
}
return rolesHash.value(role);
}
#include "kfileitemlistwidget.moc"

View file

@ -0,0 +1,118 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
***************************************************************************/
#ifndef KFILEITEMLISTWIDGET_H
#define KFILEITEMLISTWIDGET_H
#include <libdolphin_export.h>
#include <kitemviews/kitemlistwidget.h>
#include <QPixmap>
#include <QPointF>
#include <QStaticText>
class LIBDOLPHINPRIVATE_EXPORT KFileItemListWidget : public KItemListWidget
{
Q_OBJECT
public:
enum Layout
{
IconsLayout,
CompactLayout,
DetailsLayout
};
KFileItemListWidget(QGraphicsItem* parent);
virtual ~KFileItemListWidget();
void setLayout(Layout layout);
Layout layout() const;
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
virtual bool contains(const QPointF& point) const;
virtual QRectF hoverBoundingRect() const;
virtual QRectF expansionToggleRect() const;
protected:
virtual void dataChanged(const QHash<QByteArray, QVariant>& current, const QSet<QByteArray>& roles = QSet<QByteArray>());
virtual void visibleRolesChanged(const QHash<QByteArray, int>& current, const QHash<QByteArray, int>& previous);
virtual void visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current, const QHash<QByteArray, QSizeF>& previous);
virtual void styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous);
virtual void resizeEvent(QGraphicsSceneResizeEvent* event);
private:
enum TextId {
Name,
Size,
Date,
Permissions,
Owner,
Group,
Type,
Destination,
Path,
TextIdCount // Mandatory last entry
};
void updateCache();
void updateExpansionArea();
void updatePixmapCache();
void updateTextsCache();
void updateIconsLayoutTextCache();
void updateCompactLayoutTextCache();
void updateDetailsLayoutTextCache();
QString roleText(TextId textId, const QVariant& roleValue) const;
void drawPixmap(QPainter* painter, const QPixmap& pixmap);
static QPixmap pixmapForIcon(const QString& name, int size);
static TextId roleTextId(const QByteArray& role);
private:
bool m_isDir;
bool m_dirtyLayout;
bool m_dirtyContent;
QSet<QByteArray> m_dirtyContentRoles;
Layout m_layout;
QPointF m_pixmapPos;
QPixmap m_pixmap;
QSize m_scaledPixmapSize;
QRectF m_hoverPixmapRect;
QPixmap m_hoverPixmap;
QPointF m_textPos[TextIdCount];
QStaticText m_text[TextIdCount];
QRectF m_textsBoundingRect;
QList<QByteArray> m_sortedVisibleRoles;
QRectF m_expansionArea;
QColor m_additionalInfoTextColor;
};
#endif

View file

@ -0,0 +1,868 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 "kfileitemmodel.h"
#include <KDirLister>
#include <KLocale>
#include <KStringHandler>
#include <KDebug>
#include <QTimer>
#define KFILEITEMMODEL_DEBUG
KFileItemModel::KFileItemModel(KDirLister* dirLister, QObject* parent) :
KItemModelBase(QByteArray(), "name", parent),
m_dirLister(dirLister),
m_naturalSorting(true),
m_sortFoldersFirst(true),
m_groupRole(NoRole),
m_sortRole(NameRole),
m_caseSensitivity(Qt::CaseInsensitive),
m_sortedItems(),
m_items(),
m_data(),
m_requestRole(),
m_minimumUpdateIntervalTimer(0),
m_maximumUpdateIntervalTimer(0),
m_pendingItemsToInsert(),
m_pendingItemsToDelete(),
m_rootExpansionLevel(-1)
{
resetRoles();
m_requestRole[NameRole] = true;
m_requestRole[IsDirRole] = true;
Q_ASSERT(dirLister);
connect(dirLister, SIGNAL(canceled()), this, SLOT(slotCanceled()));
connect(dirLister, SIGNAL(completed()), this, SLOT(slotCompleted()));
connect(dirLister, SIGNAL(newItems(KFileItemList)), this, SLOT(slotNewItems(KFileItemList)));
connect(dirLister, SIGNAL(itemsDeleted(KFileItemList)), this, SLOT(slotItemsDeleted(KFileItemList)));
connect(dirLister, SIGNAL(clear()), this, SLOT(slotClear()));
connect(dirLister, SIGNAL(clear(KUrl)), this, SLOT(slotClear(KUrl)));
// Although the layout engine of KItemListView is fast it is very inefficient to e.g.
// emit 50 itemsInserted()-signals each 100 ms. m_minimumUpdateIntervalTimer assures that updates
// are done in 1 second intervals for equal operations.
m_minimumUpdateIntervalTimer = new QTimer(this);
m_minimumUpdateIntervalTimer->setInterval(1000);
m_minimumUpdateIntervalTimer->setSingleShot(true);
connect(m_minimumUpdateIntervalTimer, SIGNAL(timeout()), this, SLOT(dispatchPendingItems()));
// For slow KIO-slaves like used for searching it makes sense to show results periodically even
// before the completed() or canceled() signal has been emitted.
m_maximumUpdateIntervalTimer = new QTimer(this);
m_maximumUpdateIntervalTimer->setInterval(2000);
m_maximumUpdateIntervalTimer->setSingleShot(true);
connect(m_maximumUpdateIntervalTimer, SIGNAL(timeout()), this, SLOT(dispatchPendingItems()));
Q_ASSERT(m_minimumUpdateIntervalTimer->interval() <= m_maximumUpdateIntervalTimer->interval());
}
KFileItemModel::~KFileItemModel()
{
}
int KFileItemModel::count() const
{
return m_data.count();
}
QHash<QByteArray, QVariant> KFileItemModel::data(int index) const
{
if (index >= 0 && index < count()) {
return m_data.at(index);
}
return QHash<QByteArray, QVariant>();
}
bool KFileItemModel::setData(int index, const QHash<QByteArray, QVariant>& values)
{
if (index >= 0 && index < count()) {
QHash<QByteArray, QVariant> currentValue = m_data.at(index);
QSet<QByteArray> changedRoles;
QHashIterator<QByteArray, QVariant> it(values);
while (it.hasNext()) {
it.next();
const QByteArray role = it.key();
const QVariant value = it.value();
if (currentValue[role] != value) {
currentValue[role] = value;
changedRoles.insert(role);
}
}
if (!changedRoles.isEmpty()) {
m_data[index] = currentValue;
emit itemsChanged(KItemRangeList() << KItemRange(index, 1), changedRoles);
}
return true;
}
return false;
}
bool KFileItemModel::supportsGrouping() const
{
return true;
}
bool KFileItemModel::supportsSorting() const
{
return true;
}
KFileItem KFileItemModel::fileItem(int index) const
{
if (index >= 0 && index < count()) {
return m_sortedItems.at(index);
}
return KFileItem();
}
int KFileItemModel::index(const KFileItem& item) const
{
if (item.isNull()) {
return -1;
}
return m_items.value(item, -1);
}
void KFileItemModel::clear()
{
slotClear();
}
void KFileItemModel::setRoles(const QSet<QByteArray>& roles)
{
if (count() > 0) {
const bool supportedExpanding = m_requestRole[IsExpandedRole] && m_requestRole[ExpansionLevelRole];
const bool willSupportExpanding = roles.contains("isExpanded") && roles.contains("expansionLevel");
if (supportedExpanding && !willSupportExpanding) {
// No expanding is supported anymore. Take care to delete all items that have an expansion level
// that is not 0 (and hence are part of an expanded item).
removeExpandedItems();
}
}
resetRoles();
QSetIterator<QByteArray> it(roles);
while (it.hasNext()) {
const QByteArray& role = it.next();
m_requestRole[roleIndex(role)] = true;
}
if (count() > 0) {
// Update m_data with the changed requested roles
const int maxIndex = count() - 1;
for (int i = 0; i <= maxIndex; ++i) {
m_data[i] = retrieveData(m_sortedItems.at(i));
}
kWarning() << "TODO: Emitting itemsChanged() with no information what has changed!";
emit itemsChanged(KItemRangeList() << KItemRange(0, count()), QSet<QByteArray>());
}
}
QSet<QByteArray> KFileItemModel::roles() const
{
QSet<QByteArray> roles;
for (int i = 0; i < RolesCount; ++i) {
if (m_requestRole[i]) {
switch (i) {
case NoRole: break;
case NameRole: roles.insert("name"); break;
case SizeRole: roles.insert("size"); break;
case DateRole: roles.insert("date"); break;
case PermissionsRole: roles.insert("permissions"); break;
case OwnerRole: roles.insert("owner"); break;
case GroupRole: roles.insert("group"); break;
case TypeRole: roles.insert("type"); break;
case DestinationRole: roles.insert("destination"); break;
case PathRole: roles.insert("path"); break;
case IsDirRole: roles.insert("isDir"); break;
case IsExpandedRole: roles.insert("isExpanded"); break;
case ExpansionLevelRole: roles.insert("expansionLevel"); break;
default: Q_ASSERT(false); break;
}
}
}
return roles;
}
bool KFileItemModel::setExpanded(int index, bool expanded)
{
if (isExpanded(index) == expanded || index < 0 || index >= count()) {
return false;
}
QHash<QByteArray, QVariant> values;
values.insert("isExpanded", expanded);
if (!setData(index, values)) {
return false;
}
if (expanded) {
const KUrl url = m_sortedItems.at(index).url();
KDirLister* dirLister = m_dirLister.data();
if (dirLister) {
dirLister->openUrl(url, KDirLister::Keep);
return true;
}
} else {
KFileItemList itemsToRemove;
const int expansionLevel = data(index)["expansionLevel"].toInt();
++index;
while (index < count() && data(index)["expansionLevel"].toInt() > expansionLevel) {
itemsToRemove.append(m_sortedItems.at(index));
++index;
}
removeItems(itemsToRemove);
return true;
}
return false;
}
bool KFileItemModel::isExpanded(int index) const
{
if (index >= 0 && index < count()) {
return m_data.at(index).value("isExpanded").toBool();
}
return false;
}
bool KFileItemModel::isExpandable(int index) const
{
if (index >= 0 && index < count()) {
return m_sortedItems.at(index).isDir();
}
return false;
}
void KFileItemModel::onGroupRoleChanged(const QByteArray& current, const QByteArray& previous)
{
Q_UNUSED(previous);
m_groupRole = roleIndex(current);
}
void KFileItemModel::onSortRoleChanged(const QByteArray& current, const QByteArray& previous)
{
Q_UNUSED(previous);
const int itemCount = count();
if (itemCount <= 0) {
return;
}
m_sortRole = roleIndex(current);
KFileItemList sortedItems = m_sortedItems;
m_sortedItems.clear();
m_items.clear();
m_data.clear();
emit itemsRemoved(KItemRangeList() << KItemRange(0, itemCount));
sort(sortedItems.begin(), sortedItems.end());
int index = 0;
foreach (const KFileItem& item, sortedItems) {
m_sortedItems.append(item);
m_items.insert(item, index);
m_data.append(retrieveData(item));
++index;
}
emit itemsInserted(KItemRangeList() << KItemRange(0, itemCount));
}
void KFileItemModel::slotCompleted()
{
if (m_minimumUpdateIntervalTimer->isActive()) {
// dispatchPendingItems() will be called when the timer
// has been expired.
return;
}
dispatchPendingItems();
m_minimumUpdateIntervalTimer->start();
}
void KFileItemModel::slotCanceled()
{
m_minimumUpdateIntervalTimer->stop();
m_maximumUpdateIntervalTimer->stop();
dispatchPendingItems();
}
void KFileItemModel::slotNewItems(const KFileItemList& items)
{
if (!m_pendingItemsToDelete.isEmpty()) {
removeItems(m_pendingItemsToDelete);
m_pendingItemsToDelete.clear();
}
m_pendingItemsToInsert.append(items);
if (useMaximumUpdateInterval() && !m_maximumUpdateIntervalTimer->isActive()) {
// Assure that items get dispatched if no completed() or canceled() signal is
// emitted during the maximum update interval.
m_maximumUpdateIntervalTimer->start();
}
}
void KFileItemModel::slotItemsDeleted(const KFileItemList& items)
{
if (!m_pendingItemsToInsert.isEmpty()) {
insertItems(m_pendingItemsToInsert);
m_pendingItemsToInsert.clear();
}
m_pendingItemsToDelete.append(items);
}
void KFileItemModel::slotClear()
{
#ifdef KFILEITEMMODEL_DEBUG
kDebug() << "Clearing all items";
#endif
m_minimumUpdateIntervalTimer->stop();
m_maximumUpdateIntervalTimer->stop();
m_pendingItemsToInsert.clear();
m_pendingItemsToDelete.clear();
m_rootExpansionLevel = -1;
const int removedCount = m_data.count();
if (removedCount > 0) {
m_sortedItems.clear();
m_items.clear();
m_data.clear();
emit itemsRemoved(KItemRangeList() << KItemRange(0, removedCount));
}
}
void KFileItemModel::slotClear(const KUrl& url)
{
Q_UNUSED(url);
}
void KFileItemModel::dispatchPendingItems()
{
if (!m_pendingItemsToInsert.isEmpty()) {
Q_ASSERT(m_pendingItemsToDelete.isEmpty());
insertItems(m_pendingItemsToInsert);
m_pendingItemsToInsert.clear();
} else if (!m_pendingItemsToDelete.isEmpty()) {
Q_ASSERT(m_pendingItemsToInsert.isEmpty());
removeItems(m_pendingItemsToDelete);
m_pendingItemsToDelete.clear();
}
}
void KFileItemModel::insertItems(const KFileItemList& items)
{
if (items.isEmpty()) {
return;
}
#ifdef KFILEITEMMODEL_DEBUG
QElapsedTimer timer;
timer.start();
kDebug() << "===========================================================";
kDebug() << "Inserting" << items.count() << "items";
#endif
KFileItemList sortedItems = items;
sort(sortedItems.begin(), sortedItems.end());
#ifdef KFILEITEMMODEL_DEBUG
kDebug() << "[TIME] Sorting:" << timer.elapsed();
#endif
KItemRangeList itemRanges;
int targetIndex = 0;
int sourceIndex = 0;
int insertedAtIndex = -1;
int insertedCount = 0;
while (sourceIndex < sortedItems.count()) {
// Find target index from m_items to insert the current item
// in a sorted order
const int previousTargetIndex = targetIndex;
while (targetIndex < m_sortedItems.count()) {
if (!lessThan(m_sortedItems.at(targetIndex), sortedItems.at(sourceIndex))) {
break;
}
++targetIndex;
}
if (targetIndex - previousTargetIndex > 0 && insertedAtIndex >= 0) {
itemRanges << KItemRange(insertedAtIndex, insertedCount);
insertedAtIndex = targetIndex;
insertedCount = 0;
}
// Insert item at the position targetIndex
const KFileItem item = sortedItems.at(sourceIndex);
m_sortedItems.insert(targetIndex, item);
m_data.insert(targetIndex, retrieveData(item));
// m_items will be inserted after the loop (see comment below)
++insertedCount;
if (insertedAtIndex < 0) {
insertedAtIndex = targetIndex;
}
++targetIndex;
++sourceIndex;
}
// The indexes of all m_items must be adjusted, not only the index
// of the new items
for (int i = 0; i < m_sortedItems.count(); ++i) {
m_items.insert(m_sortedItems.at(i), i);
}
itemRanges << KItemRange(insertedAtIndex, insertedCount);
emit itemsInserted(itemRanges);
#ifdef KFILEITEMMODEL_DEBUG
kDebug() << "[TIME] Inserting of" << items.count() << "items:" << timer.elapsed();
#endif
}
void KFileItemModel::removeItems(const KFileItemList& items)
{
if (items.isEmpty()) {
return;
}
#ifdef KFILEITEMMODEL_DEBUG
kDebug() << "Removing " << items.count() << "items";
#endif
KFileItemList sortedItems = items;
sort(sortedItems.begin(), sortedItems.end());
QList<int> indexesToRemove;
indexesToRemove.reserve(items.count());
// Calculate the item ranges that will get deleted
KItemRangeList itemRanges;
int removedAtIndex = -1;
int removedCount = 0;
int targetIndex = 0;
foreach (const KFileItem& itemToRemove, sortedItems) {
const int previousTargetIndex = targetIndex;
while (targetIndex < m_sortedItems.count()) {
if (m_sortedItems.at(targetIndex) == itemToRemove) {
break;
}
++targetIndex;
}
if (targetIndex >= m_sortedItems.count()) {
kWarning() << "Item that should be deleted has not been found!";
return;
}
if (targetIndex - previousTargetIndex > 0 && removedAtIndex >= 0) {
itemRanges << KItemRange(removedAtIndex, removedCount);
removedAtIndex = targetIndex;
removedCount = 0;
}
indexesToRemove.append(targetIndex);
if (removedAtIndex < 0) {
removedAtIndex = targetIndex;
}
++removedCount;
++targetIndex;
}
// Delete the items
for (int i = indexesToRemove.count() - 1; i >= 0; --i) {
const int indexToRemove = indexesToRemove.at(i);
m_items.remove(m_sortedItems.at(indexToRemove));
m_sortedItems.removeAt(indexToRemove);
m_data.removeAt(indexToRemove);
}
// The indexes of all m_items must be adjusted, not only the index
// of the removed items
for (int i = 0; i < m_sortedItems.count(); ++i) {
m_items.insert(m_sortedItems.at(i), i);
}
if (count() <= 0) {
m_rootExpansionLevel = -1;
}
itemRanges << KItemRange(removedAtIndex, removedCount);
emit itemsRemoved(itemRanges);
}
void KFileItemModel::removeExpandedItems()
{
KFileItemList expandedItems;
const int maxIndex = m_data.count() - 1;
for (int i = 0; i <= maxIndex; ++i) {
if (m_data.at(i).value("expansionLevel").toInt() > 0) {
const KFileItem fileItem = m_sortedItems.at(i);
expandedItems.append(fileItem);
}
}
// The m_rootExpansionLevel may not get reset before all items with
// a bigger expansionLevel have been removed.
Q_ASSERT(m_rootExpansionLevel >= 0);
removeItems(expandedItems);
m_rootExpansionLevel = -1;
}
void KFileItemModel::resetRoles()
{
for (int i = 0; i < RolesCount; ++i) {
m_requestRole[i] = false;
}
}
KFileItemModel::Role KFileItemModel::roleIndex(const QByteArray& role) const
{
static QHash<QByteArray, Role> rolesHash;
if (rolesHash.isEmpty()) {
rolesHash.insert("name", NameRole);
rolesHash.insert("size", SizeRole);
rolesHash.insert("date", DateRole);
rolesHash.insert("permissions", PermissionsRole);
rolesHash.insert("owner", OwnerRole);
rolesHash.insert("group", GroupRole);
rolesHash.insert("type", TypeRole);
rolesHash.insert("destination", DestinationRole);
rolesHash.insert("path", PathRole);
rolesHash.insert("isDir", IsDirRole);
rolesHash.insert("isExpanded", IsExpandedRole);
rolesHash.insert("expansionLevel", ExpansionLevelRole);
}
return rolesHash.value(role, NoRole);
}
QHash<QByteArray, QVariant> KFileItemModel::retrieveData(const KFileItem& item) const
{
// It is important to insert only roles that are fast to retrieve. E.g.
// KFileItem::iconName() can be very expensive if the MIME-type is unknown
// and hence will be retrieved asynchronously by KFileItemModelRolesUpdater.
QHash<QByteArray, QVariant> data;
data.insert("iconPixmap", QPixmap());
const bool isDir = item.isDir();
if (m_requestRole[IsDirRole]) {
data.insert("isDir", isDir);
}
if (m_requestRole[NameRole]) {
data.insert("name", item.name());
}
if (m_requestRole[SizeRole]) {
if (isDir) {
data.insert("size", QVariant());
} else {
data.insert("size", item.size());
}
}
if (m_requestRole[DateRole]) {
// Don't use KFileItem::timeString() as this is too expensive when
// having several thousands of items. Instead the formatting of the
// date-time will be done on-demand by the view when the date will be shown.
const KDateTime dateTime = item.time(KFileItem::ModificationTime);
data.insert("date", dateTime.dateTime());
}
if (m_requestRole[PermissionsRole]) {
data.insert("permissions", item.permissionsString());
}
if (m_requestRole[OwnerRole]) {
data.insert("owner", item.user());
}
if (m_requestRole[GroupRole]) {
data.insert("group", item.group());
}
if (m_requestRole[DestinationRole]) {
QString destination = item.linkDest();
if (destination.isEmpty()) {
destination = i18nc("@item:intable", "No destination");
}
data.insert("destination", destination);
}
if (m_requestRole[PathRole]) {
data.insert("path", item.localPath());
}
if (m_requestRole[IsExpandedRole]) {
data.insert("isExpanded", false);
}
if (m_requestRole[ExpansionLevelRole]) {
if (m_rootExpansionLevel < 0) {
KDirLister* dirLister = m_dirLister.data();
if (dirLister) {
const QString rootDir = dirLister->url().directory(KUrl::AppendTrailingSlash);
m_rootExpansionLevel = rootDir.count('/');
}
}
const QString dir = item.url().directory(KUrl::AppendTrailingSlash);
const int level = dir.count('/') - m_rootExpansionLevel - 1;
data.insert("expansionLevel", level);
}
if (item.isMimeTypeKnown()) {
data.insert("iconName", item.iconName());
if (m_requestRole[TypeRole]) {
data.insert("type", item.mimeComment());
}
}
return data;
}
bool KFileItemModel::lessThan(const KFileItem& a, const KFileItem& b) const
{
int result = 0;
if (m_rootExpansionLevel >= 0) {
result = expansionLevelsCompare(a, b);
if (result != 0) {
// The items have parents with different expansion levels
return result < 0;
}
}
if (m_sortFoldersFirst) {
const bool isDirA = a.isDir();
const bool isDirB = b.isDir();
if (isDirA && !isDirB) {
return true;
} else if (!isDirA && isDirB) {
return false;
}
}
switch (m_sortRole) {
case NameRole: {
result = stringCompare(a.text(), b.text());
if (result == 0) {
// KFileItem::text() may not be unique in case UDS_DISPLAY_NAME is used
result = stringCompare(a.name(m_caseSensitivity == Qt::CaseInsensitive),
b.name(m_caseSensitivity == Qt::CaseInsensitive));
}
break;
}
case DateRole: {
const KDateTime dateTimeA = a.time(KFileItem::ModificationTime);
const KDateTime dateTimeB = b.time(KFileItem::ModificationTime);
if (dateTimeA < dateTimeB) {
result = -1;
} else if (dateTimeA > dateTimeB) {
result = +1;
}
break;
}
default:
break;
}
if (result == 0) {
// It must be assured that the sort order is always unique even if two values have been
// equal. In this case a comparison of the URL is done which is unique in all cases
// within KDirLister.
result = QString::compare(a.url().url(), b.url().url(), Qt::CaseSensitive);
}
return result < 0;
}
void KFileItemModel::sort(const KFileItemList::iterator& startIterator, const KFileItemList::iterator& endIterator)
{
KFileItemList::iterator start = startIterator;
KFileItemList::iterator end = endIterator;
// The implementation is based on qSortHelper() from qalgorithms.h
// Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
// In opposite to qSort() it allows to use a member-function for the comparison of elements.
while (1) {
int span = int(end - start);
if (span < 2) {
return;
}
--end;
KFileItemList::iterator low = start, high = end - 1;
KFileItemList::iterator pivot = start + span / 2;
if (lessThan(*end, *start)) {
qSwap(*end, *start);
}
if (span == 2) {
return;
}
if (lessThan(*pivot, *start)) {
qSwap(*pivot, *start);
}
if (lessThan(*end, *pivot)) {
qSwap(*end, *pivot);
}
if (span == 3) {
return;
}
qSwap(*pivot, *end);
while (low < high) {
while (low < high && lessThan(*low, *end)) {
++low;
}
while (high > low && lessThan(*end, *high)) {
--high;
}
if (low < high) {
qSwap(*low, *high);
++low;
--high;
} else {
break;
}
}
if (lessThan(*low, *end)) {
++low;
}
qSwap(*end, *low);
sort(start, low);
start = low + 1;
++end;
}
}
int KFileItemModel::stringCompare(const QString& a, const QString& b) const
{
// Taken from KDirSortFilterProxyModel (kdelibs/kfile/kdirsortfilterproxymodel.*)
// Copyright (C) 2006 by Peter Penz <peter.penz@gmx.at>
// Copyright (C) 2006 by Dominic Battre <dominic@battre.de>
// Copyright (C) 2006 by Martin Pool <mbp@canonical.com>
if (m_caseSensitivity == Qt::CaseInsensitive) {
const int result = m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseInsensitive)
: QString::compare(a, b, Qt::CaseInsensitive);
if (result != 0) {
// Only return the result, if the strings are not equal. If they are equal by a case insensitive
// comparison, still a deterministic sort order is required. A case sensitive
// comparison is done as fallback.
return result;
}
}
return m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseSensitive)
: QString::compare(a, b, Qt::CaseSensitive);
}
int KFileItemModel::expansionLevelsCompare(const KFileItem& a, const KFileItem& b) const
{
const KUrl urlA = a.url();
const KUrl urlB = b.url();
if (urlA.directory() == urlB.directory()) {
// Both items have the same directory as parent
return 0;
}
// Check whether one item is the parent of the other item
if (urlA.isParentOf(urlB)) {
return -1;
} else if (urlB.isParentOf(urlA)) {
return +1;
}
// Determine the maximum common path of both items and
// remember the index in 'index'
const QString pathA = urlA.path();
const QString pathB = urlB.path();
const int maxIndex = qMin(pathA.length(), pathB.length()) - 1;
int index = 0;
while (index <= maxIndex && pathA.at(index) == pathB.at(index)) {
++index;
}
if (index > maxIndex) {
index = maxIndex;
}
while (pathA.at(index) != QLatin1Char('/') && index > 0) {
--index;
}
// Determine the first sub-path after the common path and
// check whether it represents a directory or already a file
bool isDirA = true;
const QString subPathA = subPath(a, pathA, index, &isDirA);
bool isDirB = true;
const QString subPathB = subPath(b, pathB, index, &isDirB);
if (isDirA && !isDirB) {
return -1;
} else if (!isDirA && isDirB) {
return +1;
}
return stringCompare(subPathA, subPathB);
}
QString KFileItemModel::subPath(const KFileItem& item,
const QString& itemPath,
int start,
bool* isDir) const
{
Q_ASSERT(isDir);
const int pathIndex = itemPath.indexOf('/', start + 1);
*isDir = (pathIndex > 0) || item.isDir();
return itemPath.mid(start, pathIndex - start);
}
bool KFileItemModel::useMaximumUpdateInterval() const
{
const KDirLister* dirLister = m_dirLister.data();
return dirLister && !dirLister->url().isLocalFile();
}
#include "kfileitemmodel.moc"

View file

@ -0,0 +1,192 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
***************************************************************************/
#ifndef KFILEITEMMODEL_H
#define KFILEITEMMODEL_H
#include <libdolphin_export.h>
#include <KFileItemList>
#include <KUrl>
#include <kitemviews/kitemmodelbase.h>
#include <QHash>
class KDirLister;
class QTimer;
/**
* @brief KItemModelBase implementation for KFileItems.
*
* KFileItemModel is connected with one KDirLister. Each time the KDirLister
* emits new items, removes items or changes items the model gets synchronized.
*
* KFileItemModel supports sorting and grouping of items. Additional roles that
* are not part of KFileItem can be added with KFileItemModel::setData().
*
* Also the recursive expansion of sub-directories is supported by
* KFileItemModel::setExpanded().
*/
class LIBDOLPHINPRIVATE_EXPORT KFileItemModel : public KItemModelBase
{
Q_OBJECT
public:
explicit KFileItemModel(KDirLister* dirLister, QObject* parent = 0);
virtual ~KFileItemModel();
virtual int count() const;
virtual QHash<QByteArray, QVariant> data(int index) const;
virtual bool setData(int index, const QHash<QByteArray, QVariant> &values);
/**
* @return True
* @reimp
*/
virtual bool supportsGrouping() const;
/**
* @return True
* @reimp
*/
virtual bool supportsSorting() const;
/**
* @return The file-item for the index \a index. If the index is in a valid
* range it is assured that the file-item is not null. The runtime
* complexity of this call is O(1).
*/
KFileItem fileItem(int index) const;
/**
* @return The index for the file-item \a item. -1 is returned if no file-item
* is found or if the file-item is null. The runtime
* complexity of this call is O(1).
*/
int index(const KFileItem& item) const;
/**
* Clears all items of the model.
*/
void clear();
// TODO: "name" + "isDir" is default in ctor
void setRoles(const QSet<QByteArray>& roles);
QSet<QByteArray> roles() const;
bool setExpanded(int index, bool expanded);
bool isExpanded(int index) const;
bool isExpandable(int index) const;
protected:
virtual void onGroupRoleChanged(const QByteArray& current, const QByteArray& previous);
virtual void onSortRoleChanged(const QByteArray& current, const QByteArray& previous);
private slots:
void slotCompleted();
void slotCanceled();
void slotNewItems(const KFileItemList& items);
void slotItemsDeleted(const KFileItemList& items);
void slotClear();
void slotClear(const KUrl& url);
void dispatchPendingItems();
private:
void insertItems(const KFileItemList& items);
void removeItems(const KFileItemList& items);
void removeExpandedItems();
enum Role {
NoRole,
NameRole,
SizeRole,
DateRole,
PermissionsRole,
OwnerRole,
GroupRole,
TypeRole,
DestinationRole,
PathRole,
IsDirRole,
IsExpandedRole,
ExpansionLevelRole,
RolesCount // Mandatory last entry
};
void resetRoles();
Role roleIndex(const QByteArray& role) const;
QHash<QByteArray, QVariant> retrieveData(const KFileItem& item) const;
bool lessThan(const KFileItem& a, const KFileItem& b) const;
void sort(const KFileItemList::iterator& start, const KFileItemList::iterator& end);
int stringCompare(const QString& a, const QString& b) const;
/**
* Compares the expansion level of both items. The "expansion level" is defined
* by the number of parent directories. However simply comparing just the numbers
* is not sufficient, it is also important to check the hierarchy for having
* a correct order like shown in a tree.
*/
int expansionLevelsCompare(const KFileItem& a, const KFileItem& b) const;
/**
* Helper method for expansionLevelCompare().
*/
QString subPath(const KFileItem& item,
const QString& itemPath,
int start,
bool* isDir) const;
bool useMaximumUpdateInterval() const;
private:
QWeakPointer<KDirLister> m_dirLister;
bool m_naturalSorting;
bool m_sortFoldersFirst;
Role m_groupRole;
Role m_sortRole;
Qt::CaseSensitivity m_caseSensitivity;
KFileItemList m_sortedItems; // Allows O(1) access for KFileItemModel::fileItem(int index)
QHash<KFileItem, int> m_items; // Allows O(1) access for KFileItemModel::index(const KFileItem& item)
QList<QHash<QByteArray, QVariant> > m_data;
bool m_requestRole[RolesCount];
QTimer* m_minimumUpdateIntervalTimer;
QTimer* m_maximumUpdateIntervalTimer;
KFileItemList m_pendingItemsToInsert;
KFileItemList m_pendingItemsToDelete;
// Stores the smallest expansion level of the root-URL. Is required to calculate
// the "expansionLevel" role in an efficient way. A value < 0 indicates that
// it has not been initialized yet.
mutable int m_rootExpansionLevel;
friend class KFileItemModelTest; // For unit testing
};
#endif

View file

@ -0,0 +1,765 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 "kfileitemmodelrolesupdater.h"
#include "kfileitemmodel.h"
#include "kpixmapmodifier_p.h"
#include <KConfig>
#include <KConfigGroup>
#include <KDebug>
#include <KFileItem>
#include <KGlobal>
#include <KIO/PreviewJob>
#include <QPainter>
#include <QPixmap>
#include <QElapsedTimer>
#include <QTimer>
// Required includes for subDirectoriesCount():
#ifdef Q_WS_WIN
#include <QDir>
#else
#include <dirent.h>
#include <QFile>
#endif
#define KFILEITEMMODELROLESUPDATER_DEBUG
namespace {
const int MaxResolveItemsCount = 100;
}
KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QObject* parent) :
QObject(parent),
m_paused(false),
m_previewChangedDuringPausing(false),
m_iconSizeChangedDuringPausing(false),
m_rolesChangedDuringPausing(false),
m_previewShown(false),
m_clearPreviews(false),
m_model(model),
m_iconSize(),
m_firstVisibleIndex(0),
m_lastVisibleIndex(-1),
m_roles(),
m_enabledPlugins(),
m_pendingVisibleItems(),
m_pendingInvisibleItems(),
m_previewJobs(),
m_resolvePendingRolesTimer(0)
{
Q_ASSERT(model);
const KConfigGroup globalConfig(KGlobal::config(), "PreviewSettings");
m_enabledPlugins = globalConfig.readEntry("Plugins", QStringList()
<< "directorythumbnail"
<< "imagethumbnail"
<< "jpegthumbnail");
connect(m_model, SIGNAL(itemsInserted(KItemRangeList)),
this, SLOT(slotItemsInserted(KItemRangeList)));
connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)),
this, SLOT(slotItemsRemoved(KItemRangeList)));
connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
// A timer with a minimal timeout is used to merge several triggerPendingRolesResolving() calls
// to only one call of resolvePendingRoles().
m_resolvePendingRolesTimer = new QTimer(this);
m_resolvePendingRolesTimer->setInterval(1);
m_resolvePendingRolesTimer->setSingleShot(true);
connect(m_resolvePendingRolesTimer, SIGNAL(timeout()), this, SLOT(resolvePendingRoles()));
}
KFileItemModelRolesUpdater::~KFileItemModelRolesUpdater()
{
}
void KFileItemModelRolesUpdater::setIconSize(const QSize& size)
{
if (size != m_iconSize) {
m_iconSize = size;
if (m_paused) {
m_iconSizeChangedDuringPausing = true;
} else if (m_previewShown) {
// An icon size change requires the regenerating of
// all previews
sortAndResolveAllRoles();
} else {
sortAndResolvePendingRoles();
}
}
}
QSize KFileItemModelRolesUpdater::iconSize() const
{
return m_iconSize;
}
void KFileItemModelRolesUpdater::setVisibleIndexRange(int index, int count)
{
if (index < 0) {
index = 0;
}
if (count < 0) {
count = 0;
}
if (index == m_firstVisibleIndex && count == m_lastVisibleIndex - m_firstVisibleIndex + 1) {
// The range has not been changed
return;
}
m_firstVisibleIndex = index;
m_lastVisibleIndex = qMin(index + count - 1, m_model->count() - 1);
if (hasPendingRoles() && !m_paused) {
sortAndResolvePendingRoles();
}
}
void KFileItemModelRolesUpdater::setPreviewShown(bool show)
{
if (show == m_previewShown) {
return;
}
m_previewShown = show;
if (!show) {
m_clearPreviews = true;
}
if (m_paused) {
m_previewChangedDuringPausing = true;
} else {
sortAndResolveAllRoles();
}
}
bool KFileItemModelRolesUpdater::isPreviewShown() const
{
return m_previewShown;
}
void KFileItemModelRolesUpdater::setEnabledPlugins(const QStringList& list)
{
m_enabledPlugins = list;
}
void KFileItemModelRolesUpdater::setPaused(bool paused)
{
if (paused == m_paused) {
return;
}
m_paused = paused;
if (paused) {
if (hasPendingRoles()) {
foreach (KJob* job, m_previewJobs) {
job->kill();
}
Q_ASSERT(m_previewJobs.isEmpty());
}
} else {
const bool resolveAll = (m_iconSizeChangedDuringPausing && m_previewShown) ||
(m_previewChangedDuringPausing && !m_previewShown) ||
m_rolesChangedDuringPausing;
if (resolveAll) {
sortAndResolveAllRoles();
} else {
sortAndResolvePendingRoles();
}
m_iconSizeChangedDuringPausing = false;
m_previewChangedDuringPausing = false;
m_rolesChangedDuringPausing = false;
}
}
void KFileItemModelRolesUpdater::setRoles(const QSet<QByteArray>& roles)
{
if (roles.count() == m_roles.count()) {
bool isEqual = true;
foreach (const QByteArray& role, roles) {
if (!m_roles.contains(role)) {
isEqual = false;
break;
}
}
if (isEqual) {
return;
}
}
m_roles = roles;
if (m_paused) {
m_rolesChangedDuringPausing = true;
} else {
sortAndResolveAllRoles();
}
}
QSet<QByteArray> KFileItemModelRolesUpdater::roles() const
{
return m_roles;
}
bool KFileItemModelRolesUpdater::isPaused() const
{
return m_paused;
}
QStringList KFileItemModelRolesUpdater::enabledPlugins() const
{
return m_enabledPlugins;
}
void KFileItemModelRolesUpdater::slotItemsInserted(const KItemRangeList& itemRanges)
{
// If no valid index range is given assume that all items are visible.
// A cleanup will be done later as soon as the index range has been set.
const bool hasValidIndexRange = (m_lastVisibleIndex >= 0);
if (hasValidIndexRange) {
// Move all current pending visible items that are not visible anymore
// to the pending invisible items.
QSetIterator<KFileItem> it(m_pendingVisibleItems);
while (it.hasNext()) {
const KFileItem item = it.next();
const int index = m_model->index(item);
if (index < m_firstVisibleIndex || index > m_lastVisibleIndex) {
m_pendingVisibleItems.remove(item);
m_pendingInvisibleItems.insert(item);
}
}
}
int rangesCount = 0;
foreach (const KItemRange& range, itemRanges) {
rangesCount += range.count;
// Add the inserted items to the pending visible and invisible items
const int lastIndex = range.index + range.count - 1;
for (int i = range.index; i <= lastIndex; ++i) {
const KFileItem item = m_model->fileItem(i);
if (!hasValidIndexRange || (i >= m_firstVisibleIndex && i <= m_lastVisibleIndex)) {
m_pendingVisibleItems.insert(item);
} else {
m_pendingInvisibleItems.insert(item);
}
}
}
triggerPendingRolesResolving(rangesCount);
}
void KFileItemModelRolesUpdater::slotItemsRemoved(const KItemRangeList& itemRanges)
{
Q_UNUSED(itemRanges);
m_firstVisibleIndex = 0;
m_lastVisibleIndex = -1;
if (hasPendingRoles() && m_model->count() <= 0) {
resetPendingRoles();
}
}
void KFileItemModelRolesUpdater::slotItemsChanged(const KItemRangeList& itemRanges,
const QSet<QByteArray>& roles)
{
Q_UNUSED(itemRanges);
Q_UNUSED(roles);
// TODO
}
void KFileItemModelRolesUpdater::slotGotPreview(const KFileItem& item, const QPixmap& pixmap)
{
m_pendingVisibleItems.remove(item);
m_pendingInvisibleItems.remove(item);
const int index = m_model->index(item);
if (index < 0) {
return;
}
QPixmap scaledPixmap = pixmap;
const QString mimeType = item.mimetype();
const int slashIndex = mimeType.indexOf(QLatin1Char('/'));
const QString mimeTypeGroup = mimeType.left(slashIndex);
if (mimeTypeGroup == QLatin1String("image")) {
KPixmapModifier::applyFrame(scaledPixmap, m_iconSize);
} else {
KPixmapModifier::scale(scaledPixmap, m_iconSize);
}
QHash<QByteArray, QVariant> data = rolesData(item);
data.insert("iconPixmap", scaledPixmap);
disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
m_model->setData(index, data);
connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
}
void KFileItemModelRolesUpdater::slotPreviewFailed(const KFileItem& item)
{
m_pendingVisibleItems.remove(item);
m_pendingInvisibleItems.remove(item);
const bool clearPreviews = m_clearPreviews;
m_clearPreviews = true;
applyResolvedRoles(item, ResolveAll);
m_clearPreviews = clearPreviews;
}
void KFileItemModelRolesUpdater::slotPreviewJobFinished(KJob* job)
{
#ifdef KFILEITEMMODELROLESUPDATER_DEBUG
kDebug() << "Preview job finished. Pending visible:" << m_pendingVisibleItems.count() << "invisible:" << m_pendingInvisibleItems.count();
#endif
m_previewJobs.removeOne(job);
if (!m_previewJobs.isEmpty() || !hasPendingRoles()) {
return;
}
const KFileItemList visibleItems = sortedItems(m_pendingVisibleItems);
const KFileItemList invisibleItems = itemSubSet(m_pendingInvisibleItems, MaxResolveItemsCount - visibleItems.count());
startPreviewJob(visibleItems + invisibleItems);
}
void KFileItemModelRolesUpdater::resolvePendingRoles()
{
int resolvedCount = 0;
const bool hasSlowRoles = m_previewShown
|| m_roles.contains("size")
|| m_roles.contains("type");
const ResolveHint resolveHint = hasSlowRoles ? ResolveFast : ResolveAll;
// Resolving the MIME type can be expensive. Assure that not more than 200 ms are
// spend for resolving them synchronously. Usually this is more than enough to determine
// all visible items, but there are corner cases where this limit gets easily exceeded.
const int MaxTime = 200;
QElapsedTimer timer;
timer.start();
// Resolve the MIME type of all visible items
QSetIterator<KFileItem> visibleIt(m_pendingVisibleItems);
while (visibleIt.hasNext()) {
const KFileItem item = visibleIt.next();
applyResolvedRoles(item, resolveHint);
if (!hasSlowRoles) {
Q_ASSERT(!m_pendingInvisibleItems.contains(item));
// All roles have been resolved already by applyResolvedRoles()
m_pendingVisibleItems.remove(item);
}
++resolvedCount;
if (timer.elapsed() > MaxTime) {
break;
}
}
// Resolve the MIME type of the invisible items at least until the timeout
// has been exceeded or the maximum number of items has been reached
KFileItemList invisibleItems;
if (m_lastVisibleIndex >= 0) {
// The visible range is valid, don't care about the order how the MIME
// type of invisible items get resolved
invisibleItems = m_pendingInvisibleItems.toList();
} else {
// The visible range is temporary invalid (e.g. happens when loading
// a directory) so take care to sort the currently invisible items where
// a part will get visible later
invisibleItems = sortedItems(m_pendingInvisibleItems);
}
int index = 0;
while (resolvedCount < MaxResolveItemsCount && index < invisibleItems.count() && timer.elapsed() <= MaxTime) {
const KFileItem item = invisibleItems.at(index);
applyResolvedRoles(item, resolveHint);
if (!hasSlowRoles) {
// All roles have been resolved already by applyResolvedRoles()
m_pendingInvisibleItems.remove(item);
}
++index;
++resolvedCount;
}
if (m_previewShown) {
KFileItemList items = sortedItems(m_pendingVisibleItems);
items += invisibleItems;
startPreviewJob(items);
} else {
QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles()));
}
#ifdef KFILEITEMMODELROLESUPDATER_DEBUG
if (timer.elapsed() > MaxTime) {
kDebug() << "Maximum time exceeded, skipping items... Remaining visible:" << m_pendingVisibleItems.count()
<< "invisible:" << m_pendingInvisibleItems.count();
}
kDebug() << "[TIME] Resolved pending roles:" << timer.elapsed();
#endif
}
void KFileItemModelRolesUpdater::resolveNextPendingRoles()
{
if (m_paused) {
return;
}
if (m_previewShown) {
// The preview has been turned on since the last run. Skip
// resolving further pending roles as this is done as soon
// as a preview has been received.
return;
}
int resolvedCount = 0;
bool changed = false;
for (int i = 0; i <= 1; ++i) {
QSet<KFileItem>& pendingItems = (i == 0) ? m_pendingVisibleItems : m_pendingInvisibleItems;
QSetIterator<KFileItem> it(pendingItems);
while (it.hasNext() && !changed && resolvedCount < MaxResolveItemsCount) {
const KFileItem item = it.next();
pendingItems.remove(item);
changed = applyResolvedRoles(item, ResolveAll);
++resolvedCount;
}
}
if (hasPendingRoles()) {
QTimer::singleShot(0, this, SLOT(resolveNextPendingRoles()));
} else {
m_clearPreviews = false;
}
#ifdef KFILEITEMMODELROLESUPDATER_DEBUG
static int callCount = 0;
++callCount;
if (callCount % 100 == 0) {
kDebug() << "Remaining visible roles to resolve:" << m_pendingVisibleItems.count()
<< "invisible:" << m_pendingInvisibleItems.count();
}
#endif
}
void KFileItemModelRolesUpdater::startPreviewJob(const KFileItemList& items)
{
if (items.count() <= 0 || m_paused) {
return;
}
// PreviewJob internally caches items always with the size of
// 128 x 128 pixels or 256 x 256 pixels. A (slow) downscaling is done
// by PreviewJob if a smaller size is requested. For images KFileItemModelRolesUpdater must
// do a downscaling anyhow because of the frame, so in this case only the provided
// cache sizes are requested.
const QSize cacheSize = (m_iconSize.width() > 128) || (m_iconSize.height() > 128)
? QSize(256, 256) : QSize(128, 128);
KJob* job;
if (items.count() <= MaxResolveItemsCount) {
job = KIO::filePreview(items, cacheSize, &m_enabledPlugins);
} else {
KFileItemList itemsSubSet;
for (int i = 0; i <= MaxResolveItemsCount; ++i) {
itemsSubSet.append(items.at(i));
}
job = KIO::filePreview(itemsSubSet, cacheSize, &m_enabledPlugins);
}
connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)),
this, SLOT(slotGotPreview(const KFileItem&, const QPixmap&)));
connect(job, SIGNAL(failed(KFileItem)),
this, SLOT(slotPreviewFailed(KFileItem)));
connect(job, SIGNAL(finished(KJob*)),
this, SLOT(slotPreviewJobFinished(KJob*)));
m_previewJobs.append(job);
}
bool KFileItemModelRolesUpdater::hasPendingRoles() const
{
return !m_pendingVisibleItems.isEmpty() || !m_pendingInvisibleItems.isEmpty();
}
void KFileItemModelRolesUpdater::resetPendingRoles()
{
m_pendingVisibleItems.clear();
m_pendingInvisibleItems.clear();
foreach (KJob* job, m_previewJobs) {
job->kill();
}
Q_ASSERT(m_previewJobs.isEmpty());
}
void KFileItemModelRolesUpdater::triggerPendingRolesResolving(int count)
{
Q_ASSERT(count <= m_model->count());
if (count == m_model->count()) {
// When initially loading a directory a synchronous resolving prevents a minor
// flickering when opening directories. This is also fine from a performance point
// of view as it is assured in resolvePendingRoles() to never block the event-loop
// for more than 200 ms.
resolvePendingRoles();
} else {
// Items have been added. This can be done in several small steps within one loop
// because of the sorting and hence may not trigger any expensive operation.
m_resolvePendingRolesTimer->start();
}
}
void KFileItemModelRolesUpdater::sortAndResolveAllRoles()
{
if (m_paused) {
return;
}
resetPendingRoles();
Q_ASSERT(m_pendingVisibleItems.isEmpty());
Q_ASSERT(m_pendingInvisibleItems.isEmpty());
if (m_model->count() <= 0) {
return;
}
// Determine all visible items
Q_ASSERT(m_firstVisibleIndex >= 0);
for (int i = m_firstVisibleIndex; i <= m_lastVisibleIndex; ++i) {
const KFileItem item = m_model->fileItem(i);
if (!item.isNull()) {
m_pendingVisibleItems.insert(item);
}
}
// Determine all invisible items
for (int i = 0; i < m_firstVisibleIndex; ++i) {
const KFileItem item = m_model->fileItem(i);
if (!item.isNull()) {
m_pendingInvisibleItems.insert(item);
}
}
for (int i = m_lastVisibleIndex + 1; i < m_model->count(); ++i) {
const KFileItem item = m_model->fileItem(i);
if (!item.isNull()) {
m_pendingInvisibleItems.insert(item);
}
}
triggerPendingRolesResolving(m_pendingVisibleItems.count() +
m_pendingInvisibleItems.count());
}
void KFileItemModelRolesUpdater::sortAndResolvePendingRoles()
{
Q_ASSERT(!m_paused);
if (m_model->count() <= 0) {
return;
}
// If no valid index range is given assume that all items are visible.
// A cleanup will be done later as soon as the index range has been set.
const bool hasValidIndexRange = (m_lastVisibleIndex >= 0);
// Trigger a preview generation of all pending items. Assure that the visible
// pending items get generated first.
QSet<KFileItem> pendingItems;
pendingItems += m_pendingVisibleItems;
pendingItems += m_pendingInvisibleItems;
resetPendingRoles();
Q_ASSERT(m_pendingVisibleItems.isEmpty());
Q_ASSERT(m_pendingInvisibleItems.isEmpty());
QSetIterator<KFileItem> it(pendingItems);
while (it.hasNext()) {
const KFileItem item = it.next();
if (item.isNull()) {
continue;
}
const int index = m_model->index(item);
if (!hasValidIndexRange || (index >= m_firstVisibleIndex && index <= m_lastVisibleIndex)) {
m_pendingVisibleItems.insert(item);
} else {
m_pendingInvisibleItems.insert(item);
}
}
triggerPendingRolesResolving(m_pendingVisibleItems.count() +
m_pendingInvisibleItems.count());
}
bool KFileItemModelRolesUpdater::applyResolvedRoles(const KFileItem& item, ResolveHint hint)
{
const bool resolveAll = (hint == ResolveAll);
bool mimeTypeChanged = false;
if (!item.isMimeTypeKnown()) {
item.determineMimeType();
mimeTypeChanged = true;
}
if (mimeTypeChanged || resolveAll || m_clearPreviews) {
const int index = m_model->index(item);
if (index < 0) {
return false;
}
QHash<QByteArray, QVariant> data;
if (resolveAll) {
data = rolesData(item);
}
if (mimeTypeChanged || m_clearPreviews) {
data.insert("iconName", item.iconName());
}
if (m_clearPreviews) {
data.insert("iconPixmap", QString());
}
disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
m_model->setData(index, data);
connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)),
this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>)));
return true;
}
return false;
}
QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileItem& item) const
{
QHash<QByteArray, QVariant> data;
if (m_roles.contains("size")) {
if (item.isDir() && item.isLocalFile()) {
const QString path = item.localPath();
const int count = subDirectoriesCount(path);
if (count >= 0) {
data.insert("size", KIO::filesize_t(count));
}
}
}
if (m_roles.contains("type")) {
data.insert("type", item.mimeComment());
}
return data;
}
KFileItemList KFileItemModelRolesUpdater::sortedItems(const QSet<KFileItem>& items) const
{
KFileItemList itemList;
if (items.isEmpty()) {
return itemList;
}
#ifdef KFILEITEMMODELROLESUPDATER_DEBUG
QElapsedTimer timer;
timer.start();
#endif
QList<int> indexes;
indexes.reserve(items.count());
QSetIterator<KFileItem> it(items);
while (it.hasNext()) {
const KFileItem item = it.next();
const int index = m_model->index(item);
indexes.append(index);
}
qSort(indexes);
itemList.reserve(items.count());
foreach (int index, indexes) {
itemList.append(m_model->fileItem(index));
}
#ifdef KFILEITEMMODELROLESUPDATER_DEBUG
kDebug() << "[TIME] Sorting of items:" << timer.elapsed();
#endif
return itemList;
}
KFileItemList KFileItemModelRolesUpdater::itemSubSet(const QSet<KFileItem>& items, int count)
{
KFileItemList itemList;
int index = 0;
QSetIterator<KFileItem> it(items);
while (it.hasNext() && index < count) {
const KFileItem item = it.next();
if (item.isNull()) {
continue;
}
itemList.append(item);
++index;
}
return itemList;
}
int KFileItemModelRolesUpdater::subDirectoriesCount(const QString& path)
{
#ifdef Q_WS_WIN
QDir dir(path);
return dir.entryList(QDir::AllEntries|QDir::NoDotAndDotDot|QDir::System).count();
#else
// Taken from kdelibs/kio/kio/kdirmodel.cpp
// Copyright (C) 2006 David Faure <faure@kde.org>
int count = -1;
DIR* dir = ::opendir(QFile::encodeName(path));
if (dir) {
count = 0;
struct dirent *dirEntry = 0;
while ((dirEntry = ::readdir(dir))) {
if (dirEntry->d_name[0] == '.') {
if (dirEntry->d_name[1] == '\0') {
// Skip "."
continue;
}
if (dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0') {
// Skip ".."
continue;
}
}
++count;
}
::closedir(dir);
}
return count;
#endif
}
#include "kfileitemmodelrolesupdater.moc"

View file

@ -0,0 +1,177 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
***************************************************************************/
#ifndef KFILEITEMMODELROLESUPDATER_H
#define KFILEITEMMODELROLESUPDATER_H
#include <libdolphin_export.h>
#include <KFileItem>
#include <kitemviews/kitemmodelbase.h>
#include <QObject>
#include <QSet>
#include <QSize>
#include <QStringList>
class KFileItemModel;
class KJob;
class QPixmap;
class QTimer;
/**
* @brief Resolves expensive roles asynchronously and applies them to the KFileItemModel.
*
* KFileItemModel only resolves roles that are inexpensive like e.g. the file name or
* the permissions. Creating previews or determining the MIME-type can be quite expensive
* and KFileItemModelRolesUpdater takes care to update such roles asynchronously.
*/
class LIBDOLPHINPRIVATE_EXPORT KFileItemModelRolesUpdater : public QObject
{
Q_OBJECT
public:
KFileItemModelRolesUpdater(KFileItemModel* model, QObject* parent = 0);
virtual ~KFileItemModelRolesUpdater();
void setIconSize(const QSize& size);
QSize iconSize() const;
/**
* Sets the range of items that are visible currently. The roles
* of visible items are resolved first.
*/
void setVisibleIndexRange(int index, int count);
/**
* If \a show is set to true, the "iconPixmap" role will be filled with a preview
* of the file. If \a show is false the MIME type icon will be used for the "iconPixmap"
* role.
*/
void setPreviewShown(bool show);
bool isPreviewShown() const;
/**
* If \a paused is set to true the asynchronous resolving of roles will be paused.
* State changes during pauses like changing the icon size or the preview-shown
* will be remembered and handled after unpausing.
*/
void setPaused(bool paused);
bool isPaused() const;
/**
* Sets the roles that should be resolved asynchronously.
*/
void setRoles(const QSet<QByteArray>& roles);
QSet<QByteArray> roles() const;
/**
* Sets the list of enabled thumbnail plugins.
* Per default all plugins enabled in the KConfigGroup "PreviewSettings"
* are used.
*
* Note that this method doesn't cause already generated previews
* to be regenerated.
*
* For a list of available plugins, call KServiceTypeTrader::self()->query("ThumbCreator").
*
* @see enabledPlugins
*/
void setEnabledPlugins(const QStringList& list);
/**
* Returns the list of enabled thumbnail plugins.
* @see setEnabledPlugins
*/
QStringList enabledPlugins() const;
private slots:
void slotItemsInserted(const KItemRangeList& itemRanges);
void slotItemsRemoved(const KItemRangeList& itemRanges);
void slotItemsChanged(const KItemRangeList& itemRanges,
const QSet<QByteArray>& roles);
void slotGotPreview(const KFileItem& item, const QPixmap& pixmap);
void slotPreviewFailed(const KFileItem& item);
/**
* Is invoked when the preview job has been finished and
* removes the job from the m_previewJobs list.
*/
void slotPreviewJobFinished(KJob* job);
void resolvePendingRoles();
void resolveNextPendingRoles();
private:
void startPreviewJob(const KFileItemList& items);
bool hasPendingRoles() const;
void resetPendingRoles();
void triggerPendingRolesResolving(int count);
void sortAndResolveAllRoles();
void sortAndResolvePendingRoles();
enum ResolveHint {
ResolveFast,
ResolveAll
};
bool applyResolvedRoles(const KFileItem& item, ResolveHint hint);
QHash<QByteArray, QVariant> rolesData(const KFileItem& item) const;
KFileItemList sortedItems(const QSet<KFileItem>& items) const;
static KFileItemList itemSubSet(const QSet<KFileItem>& items, int count);
static int subDirectoriesCount(const QString& path);
private:
// Property for setPaused()/isPaused().
bool m_paused;
// Property changes during pausing must be remembered to be able
// to react when unpausing again:
bool m_previewChangedDuringPausing;
bool m_iconSizeChangedDuringPausing;
bool m_rolesChangedDuringPausing;
// Property for setPreviewShown()/previewShown().
bool m_previewShown;
// True if the role "iconPixmap" should be cleared when resolving the next
// role with resolveRole(). Is necessary if the preview gets disabled
// during the roles-updater has been paused by setPaused().
bool m_clearPreviews;
KFileItemModel* m_model;
QSize m_iconSize;
int m_firstVisibleIndex;
int m_lastVisibleIndex;
QSet<QByteArray> m_roles;
QStringList m_enabledPlugins;
QSet<KFileItem> m_pendingVisibleItems;
QSet<KFileItem> m_pendingInvisibleItems;
QList<KJob*> m_previewJobs;
QTimer* m_resolvePendingRolesTimer;
};
#endif

View file

@ -0,0 +1,191 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* Based on the Itemviews NG project from Trolltech Labs: *
* http://qt.gitorious.org/qt-labs/itemviews-ng *
* *
* 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 "kitemlistcontainer.h"
#include "kitemlistcontroller.h"
#include "kitemlistview.h"
#include "kitemmodelbase.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QScrollBar>
#include <QStyle>
#include <KDebug>
class KItemListContainerViewport : public QGraphicsView
{
public:
KItemListContainerViewport(QGraphicsScene* scene, QWidget* parent)
: QGraphicsView(scene, parent)
{
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setViewportMargins(0, 0, 0, 0);
setFrameShape(QFrame::NoFrame);
}
void scrollContentsBy(int dx, int dy)
{
Q_UNUSED(dx);
Q_UNUSED(dy);
// Do nothing. This prevents that e.g. the wheel-event
// results in a moving of the scene items.
}
};
KItemListContainer::KItemListContainer(KItemListController* controller, QWidget* parent) :
QAbstractScrollArea(parent),
m_controller(controller)
{
Q_ASSERT(controller);
controller->setParent(this);
initialize();
}
KItemListContainer::KItemListContainer(QWidget* parent) :
QAbstractScrollArea(parent),
m_controller(0)
{
initialize();
}
KItemListContainer::~KItemListContainer()
{
}
KItemListController* KItemListContainer::controller() const
{
return m_controller;
}
void KItemListContainer::showEvent(QShowEvent* event)
{
QAbstractScrollArea::showEvent(event);
updateGeometries();
}
void KItemListContainer::resizeEvent(QResizeEvent* event)
{
QAbstractScrollArea::resizeEvent(event);
updateGeometries();
}
void KItemListContainer::scrollContentsBy(int dx, int dy)
{
KItemListView* view = m_controller->view();
if (!view) {
return;
}
const qreal currentOffset = view->offset();
const qreal offsetDiff = (view->scrollOrientation() == Qt::Vertical) ? dy : dx;
view->setOffset(currentOffset - offsetDiff);
}
void KItemListContainer::slotModelChanged(KItemModelBase* current, KItemModelBase* previous)
{
Q_UNUSED(current);
Q_UNUSED(previous);
}
void KItemListContainer::slotViewChanged(KItemListView* current, KItemListView* previous)
{
QGraphicsScene* scene = static_cast<QGraphicsView*>(viewport())->scene();
if (previous) {
scene->removeItem(previous);
disconnect(previous, SIGNAL(offsetChanged(int,int)), this, SLOT(updateScrollBars()));
disconnect(previous, SIGNAL(maximumOffsetChanged(int,int)), this, SLOT(updateScrollBars()));
}
if (current) {
scene->addItem(current);
connect(previous, SIGNAL(offsetChanged(int,int)), this, SLOT(updateScrollBars()));
connect(current, SIGNAL(maximumOffsetChanged(int,int)), this, SLOT(updateScrollBars()));
}
}
void KItemListContainer::updateScrollBars()
{
const QSizeF size = m_controller->view()->size();
if (m_controller->view()->scrollOrientation() == Qt::Vertical) {
QScrollBar* scrollBar = verticalScrollBar();
const int value = m_controller->view()->offset();
const int maximum = qMax(0, int(m_controller->view()->maximumOffset() - size.height()));
scrollBar->setPageStep(size.height());
scrollBar->setMinimum(0);
scrollBar->setMaximum(maximum);
scrollBar->setValue(value);
horizontalScrollBar()->setMaximum(0);
} else {
QScrollBar* scrollBar = horizontalScrollBar();
const int value = m_controller->view()->offset();
const int maximum = qMax(0, int(m_controller->view()->maximumOffset() - size.width()));
scrollBar->setPageStep(size.width());
scrollBar->setMinimum(0);
scrollBar->setMaximum(maximum);
scrollBar->setValue(value);
verticalScrollBar()->setMaximum(0);
}
}
void KItemListContainer::updateGeometries()
{
QRect rect = geometry();
int widthDec = frameWidth() * 2;
if (verticalScrollBar()->isVisible()) {
widthDec += style()->pixelMetric(QStyle::PM_ScrollBarExtent);
}
int heightDec = frameWidth() * 2;
if (horizontalScrollBar()->isVisible()) {
heightDec += style()->pixelMetric(QStyle::PM_ScrollBarExtent);
}
rect.adjust(0, 0, -widthDec, -heightDec);
m_controller->view()->setGeometry(QRect(0, 0, rect.width(), rect.height()));
static_cast<KItemListContainerViewport*>(viewport())->scene()->setSceneRect(0, 0, rect.width(), rect.height());
static_cast<KItemListContainerViewport*>(viewport())->viewport()->setGeometry(QRect(0, 0, rect.width(), rect.height()));
updateScrollBars();
}
void KItemListContainer::initialize()
{
if (!m_controller) {
m_controller = new KItemListController(this);
}
connect(m_controller, SIGNAL(modelChanged(KItemModelBase*,KItemModelBase*)),
this, SLOT(slotModelChanged(KItemModelBase*,KItemModelBase*)));
connect(m_controller, SIGNAL(viewChanged(KItemListView*,KItemListView*)),
this, SLOT(slotViewChanged(KItemListView*,KItemListView*)));
QGraphicsView* graphicsView = new KItemListContainerViewport(new QGraphicsScene(this), this);
setViewport(graphicsView);
}
#include "kitemlistcontainer.moc"

View file

@ -0,0 +1,70 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* Based on the Itemviews NG project from Trolltech Labs: *
* http://qt.gitorious.org/qt-labs/itemviews-ng *
* *
* 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 *
***************************************************************************/
#ifndef KITEMLISTCONTAINER_H
#define KITEMLISTCONTAINER_H
#include <libdolphin_export.h>
#include <QAbstractScrollArea>
class KItemListController;
class KItemListView;
class KItemModelBase;
/**
* @brief Provides a QWidget based scrolling view for a KItemListController.
*
* @see KItemListController
*/
class LIBDOLPHINPRIVATE_EXPORT KItemListContainer : public QAbstractScrollArea
{
Q_OBJECT
public:
explicit KItemListContainer(KItemListController* controller, QWidget* parent = 0);
KItemListContainer(QWidget* parent = 0);
virtual ~KItemListContainer();
KItemListController* controller() const;
protected:
virtual void showEvent(QShowEvent* event);
virtual void resizeEvent(QResizeEvent* event);
virtual void scrollContentsBy(int dx, int dy);
private slots:
void slotModelChanged(KItemModelBase* current, KItemModelBase* previous);
void slotViewChanged(KItemListView* current, KItemListView* previous);
void updateScrollBars();
private:
void initialize();
void updateGeometries();
private:
KItemListController* m_controller;
};
#endif

View file

@ -0,0 +1,281 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* Based on the Itemviews NG project from Trolltech Labs: *
* http://qt.gitorious.org/qt-labs/itemviews-ng *
* *
* 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 "kitemlistcontroller.h"
#include "kitemlistview.h"
#include "kitemlistselectionmanager.h"
#include <QEvent>
#include <QGraphicsSceneEvent>
#include <QTransform>
#include <KDebug>
KItemListController::KItemListController(QObject* parent) :
QObject(parent),
m_selectionBehavior(NoSelection),
m_model(0),
m_view(0),
m_selectionManager(new KItemListSelectionManager(this))
{
}
KItemListController::~KItemListController()
{
}
void KItemListController::setModel(KItemModelBase* model)
{
if (m_model == model) {
return;
}
KItemModelBase* oldModel = m_model;
m_model = model;
if (m_view) {
m_view->setModel(m_model);
}
emit modelChanged(m_model, oldModel);
}
KItemModelBase* KItemListController::model() const
{
return m_model;
}
KItemListSelectionManager* KItemListController::selectionManager() const
{
return m_selectionManager;
}
void KItemListController::setView(KItemListView* view)
{
if (m_view == view) {
return;
}
KItemListView* oldView = m_view;
m_view = view;
if (m_view) {
m_view->setController(this);
m_view->setModel(m_model);
}
emit viewChanged(m_view, oldView);
}
KItemListView* KItemListController::view() const
{
return m_view;
}
void KItemListController::setSelectionBehavior(SelectionBehavior behavior)
{
m_selectionBehavior = behavior;
}
KItemListController::SelectionBehavior KItemListController::selectionBehavior() const
{
return m_selectionBehavior;
}
bool KItemListController::showEvent(QShowEvent* event)
{
Q_UNUSED(event);
return false;
}
bool KItemListController::hideEvent(QHideEvent* event)
{
Q_UNUSED(event);
return false;
}
bool KItemListController::keyPressEvent(QKeyEvent* event)
{
Q_UNUSED(event);
return false;
}
bool KItemListController::inputMethodEvent(QInputMethodEvent* event)
{
Q_UNUSED(event);
return false;
}
bool KItemListController::mousePressEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
{
Q_UNUSED(event);
Q_UNUSED(transform);
return false;
}
bool KItemListController::mouseMoveEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
{
Q_UNUSED(event);
Q_UNUSED(transform);
return false;
}
bool KItemListController::mouseReleaseEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
{
if (m_view) {
const QPointF pos = transform.map(event->pos());
const int index = m_view->itemAt(pos);
if (index >= 0) {
bool emitItemClicked = true;
if (event->button() & Qt::LeftButton) {
if (m_view->isAboveExpansionToggle(index, pos)) {
emit itemExpansionToggleClicked(index);
emitItemClicked = false;
}
}
if (emitItemClicked) {
emit itemClicked(index, event->button());
}
}
}
return false;
}
bool KItemListController::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform)
{
Q_UNUSED(event);
Q_UNUSED(transform);
return false;
}
bool KItemListController::dragEnterEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
{
Q_UNUSED(event);
Q_UNUSED(transform);
return false;
}
bool KItemListController::dragLeaveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
{
Q_UNUSED(event);
Q_UNUSED(transform);
return false;
}
bool KItemListController::dragMoveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
{
Q_UNUSED(event);
Q_UNUSED(transform);
return false;
}
bool KItemListController::dropEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform)
{
Q_UNUSED(event);
Q_UNUSED(transform);
return false;
}
bool KItemListController::hoverEnterEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
{
Q_UNUSED(event);
Q_UNUSED(transform);
return false;
}
bool KItemListController::hoverMoveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
{
Q_UNUSED(event);
Q_UNUSED(transform);
return false;
}
bool KItemListController::hoverLeaveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform)
{
Q_UNUSED(event);
Q_UNUSED(transform);
return false;
}
bool KItemListController::wheelEvent(QGraphicsSceneWheelEvent* event, const QTransform& transform)
{
Q_UNUSED(event);
Q_UNUSED(transform);
return false;
}
bool KItemListController::resizeEvent(QGraphicsSceneResizeEvent* event, const QTransform& transform)
{
Q_UNUSED(event);
Q_UNUSED(transform);
return false;
}
bool KItemListController::processEvent(QEvent* event, const QTransform& transform)
{
if (!event) {
return false;
}
switch (event->type()) {
// case QEvent::FocusIn:
// case QEvent::FocusOut:
// return focusEvent(static_cast<QFocusEvent*>(event));
case QEvent::KeyPress:
return keyPressEvent(static_cast<QKeyEvent*>(event));
case QEvent::InputMethod:
return inputMethodEvent(static_cast<QInputMethodEvent*>(event));
case QEvent::GraphicsSceneMousePress:
return mousePressEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
case QEvent::GraphicsSceneMouseMove:
return mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
case QEvent::GraphicsSceneMouseRelease:
return mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent*>(event), QTransform());
case QEvent::GraphicsSceneWheel:
return wheelEvent(static_cast<QGraphicsSceneWheelEvent*>(event), QTransform());
case QEvent::GraphicsSceneDragEnter:
return dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
case QEvent::GraphicsSceneDragLeave:
return dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
case QEvent::GraphicsSceneDragMove:
return dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
case QEvent::GraphicsSceneDrop:
return dropEvent(static_cast<QGraphicsSceneDragDropEvent*>(event), QTransform());
case QEvent::GraphicsSceneHoverEnter:
return hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
case QEvent::GraphicsSceneHoverMove:
return hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
case QEvent::GraphicsSceneHoverLeave:
return hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent*>(event), QTransform());
case QEvent::GraphicsSceneResize:
return resizeEvent(static_cast<QGraphicsSceneResizeEvent*>(event), transform);
default:
break;
}
return false;
}
#include "kitemlistcontroller.moc"

View file

@ -0,0 +1,118 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* Based on the Itemviews NG project from Trolltech Labs: *
* http://qt.gitorious.org/qt-labs/itemviews-ng *
* *
* 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 *
***************************************************************************/
#ifndef KITEMLISTCONTROLLER_H
#define KITEMLISTCONTROLLER_H
#include <libdolphin_export.h>
#include <QObject>
class KItemModelBase;
class KItemListSelectionManager;
class KItemListView;
class QGraphicsSceneHoverEvent;
class QGraphicsSceneDragDropEvent;
class QGraphicsSceneMouseEvent;
class QGraphicsSceneResizeEvent;
class QGraphicsSceneWheelEvent;
class QHideEvent;
class QInputMethodEvent;
class QKeyEvent;
class QShowEvent;
class QTransform;
/**
* @brief Controls the view, model and selection of an item-list.
*
* For a working item-list it is mandatory to set a compatible view and model
* with KItemListController::setView() and KItemListController::setModel().
*
* @see KItemListView
* @see KItemModelBase
* @see KItemListSelectionManager
*/
class LIBDOLPHINPRIVATE_EXPORT KItemListController : public QObject
{
Q_OBJECT
Q_ENUMS(SelectionBehavior)
Q_PROPERTY(KItemModelBase* model READ model WRITE setModel)
Q_PROPERTY(KItemListView *view READ view WRITE setView)
Q_PROPERTY(SelectionBehavior selectionBehavior READ selectionBehavior WRITE setSelectionBehavior)
public:
enum SelectionBehavior {
NoSelection,
SingleSelection,
MultiSelection
};
KItemListController(QObject* parent = 0);
virtual ~KItemListController();
void setModel(KItemModelBase* model);
KItemModelBase* model() const;
void setView(KItemListView* view);
KItemListView* view() const;
KItemListSelectionManager* selectionManager() const;
void setSelectionBehavior(SelectionBehavior behavior);
SelectionBehavior selectionBehavior() const;
virtual bool showEvent(QShowEvent* event);
virtual bool hideEvent(QHideEvent* event);
virtual bool keyPressEvent(QKeyEvent* event);
virtual bool inputMethodEvent(QInputMethodEvent* event);
virtual bool mousePressEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform);
virtual bool mouseMoveEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform);
virtual bool mouseReleaseEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform);
virtual bool mouseDoubleClickEvent(QGraphicsSceneMouseEvent* event, const QTransform& transform);
virtual bool dragEnterEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform);
virtual bool dragLeaveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform);
virtual bool dragMoveEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform);
virtual bool dropEvent(QGraphicsSceneDragDropEvent* event, const QTransform& transform);
virtual bool hoverEnterEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform);
virtual bool hoverMoveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform);
virtual bool hoverLeaveEvent(QGraphicsSceneHoverEvent* event, const QTransform& transform);
virtual bool wheelEvent(QGraphicsSceneWheelEvent* event, const QTransform& transform);
virtual bool resizeEvent(QGraphicsSceneResizeEvent* event, const QTransform& transform);
virtual bool processEvent(QEvent* event, const QTransform& transform);
signals:
void itemClicked(int index, Qt::MouseButton button);
void itemExpansionToggleClicked(int index);
void modelChanged(KItemModelBase* current, KItemModelBase* previous);
void viewChanged(KItemListView* current, KItemListView* previous);
private:
SelectionBehavior m_selectionBehavior;
KItemModelBase* m_model;
KItemListView* m_view;
KItemListSelectionManager* m_selectionManager;
};
#endif

View file

@ -0,0 +1,58 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* Based on the Itemviews NG project from Trolltech Labs: *
* http://qt.gitorious.org/qt-labs/itemviews-ng *
* *
* 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 "kitemlistgroupheader.h"
#include "kitemlistview.h"
#include <QPainter>
#include <KDebug>
KItemListGroupHeader::KItemListGroupHeader(QGraphicsWidget* parent) :
QGraphicsWidget(parent, 0)
{
}
KItemListGroupHeader::~KItemListGroupHeader()
{
}
QSizeF KItemListGroupHeader::sizeHint(Qt::SizeHint which, const QSizeF& constraint) const
{
Q_UNUSED(which);
Q_UNUSED(constraint);
return QSizeF();
}
void KItemListGroupHeader::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setPen(Qt::darkGreen);
painter->setBrush(QColor(0, 255, 0, 50));
painter->drawRect(rect());
//painter->drawText(rect(), QString::number(m_index));
}
#include "kitemlistgroupheader.moc"

View file

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2006 by Peter Penz <peter.penz19@gmail.com> *
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
@ -17,50 +17,29 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#ifndef COLUMNVIEWSETTINGSPAGE_H
#define COLUMNVIEWSETTINGSPAGE_H
#ifndef KITEMLISTGROUPHEADER_H
#define KITEMLISTGROUPHEADER_H
#include "viewsettingspagebase.h"
#include <libdolphin_export.h>
class DolphinFontRequester;
class IconSizeGroupBox;
class KComboBox;
#include <QGraphicsWidget>
/**
* @brief Represents the page from the Dolphin Settings which allows
* to modify the settings for the details view.
*/
class ColumnViewSettingsPage : public ViewSettingsPageBase
class KItemListView;
class LIBDOLPHINPRIVATE_EXPORT KItemListGroupHeader : public QGraphicsWidget
{
Q_OBJECT
public:
ColumnViewSettingsPage(QWidget* parent);
virtual ~ColumnViewSettingsPage();
KItemListGroupHeader(QGraphicsWidget* parent = 0);
virtual ~KItemListGroupHeader();
/**
* Applies the settings for the details view.
* The settings are persisted automatically when
* closing Dolphin.
*/
virtual void applySettings();
void setIndex(int index);
int index() const;
/** Restores the settings to default values. */
virtual void restoreDefaults();
private:
void loadSettings();
private:
enum
{
BaseTextWidth = 200,
TextInc = 50
};
IconSizeGroupBox* m_iconSizeGroupBox;
DolphinFontRequester* m_fontRequester;
KComboBox* m_textWidthBox;
virtual QSizeF sizeHint(Qt::SizeHint which = Qt::PreferredSize, const QSizeF& constraint = QSizeF()) const;
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
};
#endif

View file

@ -0,0 +1,87 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* Based on the Itemviews NG project from Trolltech Labs: *
* http://qt.gitorious.org/qt-labs/itemviews-ng *
* *
* 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 "kitemlistselectionmanager.h"
#include "kitemmodelbase.h"
KItemListSelectionManager::KItemListSelectionManager(QObject* parent) :
QObject(parent),
m_currentItem(-1),
m_anchorItem(-1),
m_model(0)
{
}
KItemListSelectionManager::~KItemListSelectionManager()
{
}
void KItemListSelectionManager::setCurrentItem(int current)
{
const int previous = m_currentItem;
if (m_model && current < m_model->count()) {
m_currentItem = current;
} else {
m_currentItem = -1;
}
if (m_currentItem != previous) {
emit currentChanged(m_currentItem, previous);
}
}
int KItemListSelectionManager::currentItem() const
{
return m_currentItem;
}
void KItemListSelectionManager::setAnchorItem(int anchor)
{
const int previous = m_anchorItem;
if (m_model && anchor < m_model->count()) {
m_anchorItem = anchor;
} else {
m_anchorItem = -1;
}
if (m_anchorItem != previous) {
emit anchorChanged(m_anchorItem, previous);
}
}
int KItemListSelectionManager::anchorItem() const
{
return m_anchorItem;
}
KItemModelBase* KItemListSelectionManager::model() const
{
return m_model;
}
void KItemListSelectionManager::setModel(KItemModelBase* model)
{
m_model = model;
}
#include "kitemlistselectionmanager.moc"

View file

@ -0,0 +1,69 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* Based on the Itemviews NG project from Trolltech Labs: *
* http://qt.gitorious.org/qt-labs/itemviews-ng *
* *
* 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 *
***************************************************************************/
#ifndef KITEMLISTSELECTIONMANAGER_H
#define KITEMLISTSELECTIONMANAGER_H
#include <libdolphin_export.h>
#include <QObject>
class KItemModelBase;
class LIBDOLPHINPRIVATE_EXPORT KItemListSelectionManager : public QObject
{
Q_OBJECT
public:
enum SelectionMode {
Select,
Deselect,
Toggle
};
KItemListSelectionManager(QObject* parent = 0);
virtual ~KItemListSelectionManager();
void setCurrentItem(int current);
int currentItem() const;
void setAnchorItem(int anchor);
int anchorItem() const;
KItemModelBase* model() const;
signals:
void currentChanged(int current, int previous);
void anchorChanged(int anchor, int previous);
protected:
void setModel(KItemModelBase* model);
private:
int m_currentItem;
int m_anchorItem;
KItemModelBase* m_model;
friend class KItemListController;
};
#endif

View file

@ -0,0 +1,86 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 "kitemlistsizehintresolver_p.h"
#include <kitemviews/kitemlistview.h>
#include <KDebug>
KItemListSizeHintResolver::KItemListSizeHintResolver(const KItemListView* itemListView) :
m_itemListView(itemListView),
m_sizeHintCache()
{
}
KItemListSizeHintResolver::~KItemListSizeHintResolver()
{
}
QSizeF KItemListSizeHintResolver::sizeHint(int index) const
{
QSizeF size = m_sizeHintCache.at(index);
if (size.isEmpty()) {
size = m_itemListView->itemSizeHint(index);
m_sizeHintCache[index] = size;
}
return size;
}
void KItemListSizeHintResolver::itemsInserted(int index, int count)
{
const int currentCount = m_sizeHintCache.count();
m_sizeHintCache.reserve(currentCount + count);
while (count > 0) {
m_sizeHintCache.insert(index, QSizeF());
++index;
--count;
}
}
void KItemListSizeHintResolver::itemsRemoved(int index, int count)
{
const QList<QSizeF>::iterator begin = m_sizeHintCache.begin() + index;
const QList<QSizeF>::iterator end = begin + count;
m_sizeHintCache.erase(begin, end);
}
void KItemListSizeHintResolver::itemsMoved(int from, int to, int count)
{
Q_UNUSED(from);
Q_UNUSED(to);
Q_UNUSED(count);
}
void KItemListSizeHintResolver::itemsChanged(int index, int count, const QSet<QByteArray>& roles)
{
Q_UNUSED(roles);
while (count) {
m_sizeHintCache[index] = QSizeF();
++index;
--count;
}
}
void KItemListSizeHintResolver::clearCache()
{
const int count = m_sizeHintCache.count();
for (int i = 0; i < count; ++i) {
m_sizeHintCache[i] = QSizeF();
}
}

View file

@ -0,0 +1,50 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
***************************************************************************/
#ifndef KITEMLISTSIZEHINTRESOLVER_H
#define KITEMLISTSIZEHINTRESOLVER_H
#include <libdolphin_export.h>
#include <QByteArray>
#include <QList>
#include <QSizeF>
class KItemListView;
class LIBDOLPHINPRIVATE_EXPORT KItemListSizeHintResolver
{
public:
KItemListSizeHintResolver(const KItemListView* itemListView);
virtual ~KItemListSizeHintResolver();
QSizeF sizeHint(int index) const;
void itemsInserted(int index, int count);
void itemsRemoved(int index, int count);
void itemsMoved(int from, int to, int count);
void itemsChanged(int index, int count, const QSet<QByteArray>& roles);
void clearCache();
private:
const KItemListView* m_itemListView;
mutable QList<QSizeF> m_sizeHintCache;
};
#endif

View file

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2010 by Frank Reininghaus (frank78ac@googlemail.com) *
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
@ -17,26 +17,21 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include <qtest_kde.h>
#include "kitemliststyleoption.h"
#include "dolphinviewtest_allviewmodes.h"
class DolphinViewTest_Details : public DolphinViewTest_AllViewModes
KItemListStyleOption::KItemListStyleOption() :
QStyleOption(QStyleOption::Version, QStyleOption::SO_CustomBase + 1)
{
Q_OBJECT
}
public:
KItemListStyleOption::KItemListStyleOption(const KItemListStyleOption& other) :
QStyleOption(other)
{
margin = other.margin;
iconSize = other.iconSize;
font = other.font;
}
virtual DolphinView::Mode mode() const {
return DolphinView::DetailsView;
}
virtual bool verifyCorrectViewMode(const DolphinView* view) const {
return (view->mode() == DolphinView::DetailsView);
}
};
QTEST_KDEMAIN(DolphinViewTest_Details, GUI)
#include "dolphinviewtest_details.moc"
KItemListStyleOption::~KItemListStyleOption()
{
}

View file

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2010 by Frank Reininghaus (frank78ac@googlemail.com) *
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
@ -17,26 +17,25 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include <qtest_kde.h>
#ifndef KITEMLISTSTYLEOPTION_H
#define KITEMLISTSTYLEOPTION_H
#include "dolphinviewtest_allviewmodes.h"
#include <libdolphin_export.h>
class DolphinViewTest_Columns : public DolphinViewTest_AllViewModes
#include <QFont>
#include <QStyleOption>
class LIBDOLPHINPRIVATE_EXPORT KItemListStyleOption : public QStyleOption
{
Q_OBJECT
public:
KItemListStyleOption();
KItemListStyleOption(const KItemListStyleOption& other);
virtual ~KItemListStyleOption();
virtual DolphinView::Mode mode() const {
return DolphinView::ColumnView;
}
virtual bool verifyCorrectViewMode(const DolphinView* view) const {
return (view->mode() == DolphinView::ColumnView);
}
int margin;
int iconSize;
QFont font;
};
#endif
QTEST_KDEMAIN(DolphinViewTest_Columns, GUI)
#include "dolphinviewtest_columns.moc"

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,360 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* Based on the Itemviews NG project from Trolltech Labs: *
* http://qt.gitorious.org/qt-labs/itemviews-ng *
* *
* 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 *
***************************************************************************/
#ifndef KITEMLISTVIEW_H
#define KITEMLISTVIEW_H
#include <libdolphin_export.h>
#include <kitemviews/kitemliststyleoption.h>
#include <kitemviews/kitemlistviewanimation_p.h>
#include <kitemviews/kitemlistwidget.h>
#include <kitemviews/kitemmodelbase.h>
#include <QGraphicsWidget>
class KItemListController;
class KItemListWidgetCreatorBase;
class KItemListGroupHeader;
class KItemListGroupHeaderCreatorBase;
class KItemListSizeHintResolver;
class KItemListViewAnimation;
class KItemListViewLayouter;
class KItemListWidget;
class KItemListViewCreatorBase;
class QTimer;
/**
* @brief Represents the view of an item-list.
*
* The view is responsible for showing the items of the model within
* a GraphicsItem. Each visible item is represented by a KItemListWidget.
*
* The created view must be applied to the KItemListController with
* KItemListController::setView(). For showing a custom model it is not
* mandatory to derive from KItemListView, all that is necessary is
* to set a widget-creator that is capable to create KItemListWidgets
* showing the model items. A widget-creator can be set with
* KItemListView::setWidgetCreator().
*
* @see KItemListWidget
* @see KItemModelBase
*/
class LIBDOLPHINPRIVATE_EXPORT KItemListView : public QGraphicsWidget
{
Q_OBJECT
public:
KItemListView(QGraphicsWidget* parent = 0);
virtual ~KItemListView();
void setScrollOrientation(Qt::Orientation orientation);
Qt::Orientation scrollOrientation() const;
void setItemSize(const QSizeF& size);
QSizeF itemSize() const;
// TODO: add note that offset is not checked against maximumOffset, only against 0.
void setOffset(qreal offset);
qreal offset() const;
qreal maximumOffset() const;
/**
* Sets the visible roles to \p roles. The integer-value defines
* the order of the visible role: Smaller values are ordered first.
*/
void setVisibleRoles(const QHash<QByteArray, int>& roles);
QHash<QByteArray, int> visibleRoles() const;
/**
* @return Controller of the item-list. The controller gets
* initialized by KItemListController::setView() and will
* result in calling KItemListController::onControllerChanged().
*/
KItemListController* controller() const;
/**
* @return Model of the item-list. The model gets
* initialized by KItemListController::setView() and will
* result in calling KItemListController::onModelChanged().
*/
KItemModelBase* model() const;
/**
* Sets the creator that creates a widget showing the
* content of one model-item. Usually it is sufficient
* to implement a custom widget X derived from KItemListWidget and
* set the creator by:
* <code>
* itemListView->setWidgetCreator(new KItemListWidgetCreator<X>());
* </code>
* Note that the ownership of the widget creator is not transferred to
* the item-list view: One instance of a widget creator might get shared
* by several item-list view instances.
**/
void setWidgetCreator(KItemListWidgetCreatorBase* widgetCreator);
KItemListWidgetCreatorBase* widgetCreator() const;
void setGroupHeaderCreator(KItemListGroupHeaderCreatorBase* groupHeaderCreator);
KItemListGroupHeaderCreatorBase* groupHeaderCreator() const;
void setStyleOption(const KItemListStyleOption& option);
const KItemListStyleOption& styleOption() const;
virtual void setGeometry(const QRectF& rect);
int itemAt(const QPointF& pos) const;
bool isAboveSelectionToggle(int index, const QPointF& pos) const;
bool isAboveExpansionToggle(int index, const QPointF& pos) const;
int firstVisibleIndex() const;
int lastVisibleIndex() const;
virtual QSizeF itemSizeHint(int index) const;
virtual QHash<QByteArray, QSizeF> visibleRoleSizes() const;
void beginTransaction();
void endTransaction();
bool isTransactionActive() const;
signals:
void offsetChanged(int current, int previous);
void maximumOffsetChanged(int current, int previous);
protected:
virtual void initializeItemListWidget(KItemListWidget* item);
virtual void onControllerChanged(KItemListController* current, KItemListController* previous);
virtual void onModelChanged(KItemModelBase* current, KItemModelBase* previous);
virtual void onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous);
virtual void onItemSizeChanged(const QSizeF& current, const QSizeF& previous);
virtual void onOffsetChanged(qreal current, qreal previous);
virtual void onVisibleRolesChanged(const QHash<QByteArray, int>& current, const QHash<QByteArray, int>& previous);
virtual void onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous);
virtual void onTransactionBegin();
virtual void onTransactionEnd();
virtual bool event(QEvent* event);
virtual void mousePressEvent(QGraphicsSceneMouseEvent* event);
virtual void hoverMoveEvent(QGraphicsSceneHoverEvent* event);
virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* event);
QList<KItemListWidget*> visibleItemListWidgets() const;
protected slots:
virtual void slotItemsInserted(const KItemRangeList& itemRanges);
virtual void slotItemsRemoved(const KItemRangeList& itemRanges);
virtual void slotItemsChanged(const KItemRangeList& itemRanges,
const QSet<QByteArray>& roles);
private slots:
void slotAnimationFinished(QGraphicsWidget* widget,
KItemListViewAnimation::AnimationType type);
void slotLayoutTimerFinished();
private:
enum LayoutAnimationHint
{
NoAnimation,
Animation
};
enum SizeType
{
LayouterSize,
ItemSize
};
void setController(KItemListController* controller);
void setModel(KItemModelBase* model);
void updateLayout();
void doLayout(LayoutAnimationHint hint, int changedIndex, int changedCount);
void doGroupHeadersLayout(LayoutAnimationHint hint, int changedIndex, int changedCount);
void emitOffsetChanges();
KItemListWidget* createWidget(int index);
void recycleWidget(KItemListWidget* widget);
void setWidgetIndex(KItemListWidget* widget, int index);
/**
* Helper method for setGeometry() and setItemSize(): Calling both methods might result
* in a changed number of visible items. To assure that currently invisible items can
* get animated from the old position to the new position prepareLayoutForIncreasedItemCount()
* takes care to create all item widgets that are visible with the old or the new size.
* @param size Size of the layouter or the item dependent on \p sizeType.
* @param sizeType LayouterSize: KItemListLayouter::setSize() is used.
* ItemSize: KItemListLayouter::setItemSize() is used.
*/
void prepareLayoutForIncreasedItemCount(const QSizeF& size, SizeType sizeType);
/**
* Helper method for prepareLayoutForIncreasedItemCount().
*/
void setLayouterSize(const QSizeF& size, SizeType sizeType);
/**
* Marks the visible roles as dirty so that they will get updated when doing the next
* layout. The visible roles will only get marked as dirty if an empty item-size is
* given.
* @return True if the visible roles have been marked as dirty.
*/
bool markVisibleRolesSizesAsDirty();
/**
* Updates the m_visibleRoleSizes property and applies the dynamic
* size to the layouter.
*/
void applyDynamicItemSize();
private:
bool m_grouped;
int m_activeTransactions; // Counter for beginTransaction()/endTransaction()
QSizeF m_itemSize;
KItemListController* m_controller;
KItemModelBase* m_model;
QHash<QByteArray, int> m_visibleRoles;
QHash<QByteArray, QSizeF> m_visibleRolesSizes;
KItemListWidgetCreatorBase* m_widgetCreator;
KItemListGroupHeaderCreatorBase* m_groupHeaderCreator;
KItemListStyleOption m_styleOption;
QHash<int, KItemListWidget*> m_visibleItems;
QHash<KItemListWidget*, KItemListGroupHeader*> m_visibleGroups;
int m_scrollBarExtent;
KItemListSizeHintResolver* m_sizeHintResolver;
KItemListViewLayouter* m_layouter;
KItemListViewAnimation* m_animation;
QTimer* m_layoutTimer; // Triggers an asynchronous doLayout() call.
int m_oldOffset;
int m_oldMaximumOffset;
friend class KItemListController;
};
/**
* Allows to do a fast logical creation and deletion of QGraphicsWidgets
* by recycling existing QGraphicsWidgets instances. Is used by
* KItemListWidgetCreatorBase and KItemListGroupHeaderCreatorBase.
* @internal
*/
class LIBDOLPHINPRIVATE_EXPORT KItemListCreatorBase
{
public:
virtual ~KItemListCreatorBase();
protected:
void addCreatedWidget(QGraphicsWidget* widget);
void pushRecycleableWidget(QGraphicsWidget* widget);
QGraphicsWidget* popRecycleableWidget();
private:
QSet<QGraphicsWidget*> m_createdWidgets;
QList<QGraphicsWidget*> m_recycleableWidgets;
};
/**
* @brief Base class for creating KItemListWidgets.
*
* It is recommended that applications simply use the KItemListWidgetCreator-template class.
* For a custom implementation the methods create() and recyle() must be reimplemented.
* The intention of the widget creator is to prevent repetitive and expensive instantiations and
* deletions of KItemListWidgets by recycling existing widget instances.
*/
class LIBDOLPHINPRIVATE_EXPORT KItemListWidgetCreatorBase : public KItemListCreatorBase
{
public:
virtual ~KItemListWidgetCreatorBase();
virtual KItemListWidget* create(KItemListView* view) = 0;
virtual void recycle(KItemListWidget* widget);
};
template <class T>
class LIBDOLPHINPRIVATE_EXPORT KItemListWidgetCreator : public KItemListWidgetCreatorBase
{
public:
virtual ~KItemListWidgetCreator();
virtual KItemListWidget* create(KItemListView* view);
};
template <class T>
KItemListWidgetCreator<T>::~KItemListWidgetCreator()
{
}
template <class T>
KItemListWidget* KItemListWidgetCreator<T>::create(KItemListView* view)
{
KItemListWidget* widget = static_cast<KItemListWidget*>(popRecycleableWidget());
if (!widget) {
widget = new T(view);
addCreatedWidget(widget);
}
return widget;
}
/**
* @brief Base class for creating KItemListGroupHeaders.
*
* It is recommended that applications simply use the KItemListGroupHeaderCreator-template class.
* For a custom implementation the methods create() and recyle() must be reimplemented.
* The intention of the group-header creator is to prevent repetitive and expensive instantiations and
* deletions of KItemListGroupHeaders by recycling existing header instances.
*/
class LIBDOLPHINPRIVATE_EXPORT KItemListGroupHeaderCreatorBase : public KItemListCreatorBase
{
public:
virtual ~KItemListGroupHeaderCreatorBase();
virtual KItemListGroupHeader* create(QGraphicsWidget* parent) = 0;
virtual void recycle(KItemListGroupHeader* header);
};
template <class T>
class LIBDOLPHINPRIVATE_EXPORT KItemListGroupHeaderCreator : public KItemListGroupHeaderCreatorBase
{
public:
virtual ~KItemListGroupHeaderCreator();
virtual KItemListGroupHeader* create(QGraphicsWidget* parent);
};
template <class T>
KItemListGroupHeaderCreator<T>::~KItemListGroupHeaderCreator()
{
}
template <class T>
KItemListGroupHeader* KItemListGroupHeaderCreator<T>::create(QGraphicsWidget* parent)
{
KItemListGroupHeader* widget = static_cast<KItemListGroupHeader*>(popRecycleableWidget());
if (!widget) {
widget = new T(parent);
addCreatedWidget(widget);
}
return widget;
}
#endif

View file

@ -0,0 +1,241 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 "kitemlistviewanimation_p.h"
#include "kitemlistview.h"
#include <KDebug>
#include <QGraphicsWidget>
#include <QPropertyAnimation>
KItemListViewAnimation::KItemListViewAnimation(QObject* parent) :
QObject(parent),
m_scrollOrientation(Qt::Vertical),
m_offset(0),
m_animation()
{
}
KItemListViewAnimation::~KItemListViewAnimation()
{
for (int type = 0; type < AnimationTypeCount; ++type) {
qDeleteAll(m_animation[type]);
}
}
void KItemListViewAnimation::setScrollOrientation(Qt::Orientation orientation)
{
m_scrollOrientation = orientation;
}
Qt::Orientation KItemListViewAnimation::scrollOrientation() const
{
return m_scrollOrientation;
}
void KItemListViewAnimation::setOffset(qreal offset)
{
const qreal diff = m_offset - offset;
m_offset = offset;
// The change of the offset requires that the position of all
// animated QGraphicsWidgets get adjusted. An exception is made
// for the delete animation that should just fade away on the
// existing position.
for (int type = 0; type < AnimationTypeCount; ++type) {
if (type == DeleteAnimation) {
continue;
}
QHashIterator<QGraphicsWidget*, QPropertyAnimation*> it(m_animation[type]);
while (it.hasNext()) {
it.next();
QGraphicsWidget* widget = it.key();
QPropertyAnimation* propertyAnim = it.value();
QPointF currentPos = widget->pos();
if (m_scrollOrientation == Qt::Vertical) {
currentPos.ry() += diff;
} else {
currentPos.rx() += diff;
}
if (type == MovingAnimation) {
// Stop the animation, calculate the moved start- and end-value
// and restart the animation for the remaining duration.
const int remainingDuration = propertyAnim->duration()
- propertyAnim->currentTime();
const bool block = propertyAnim->signalsBlocked();
propertyAnim->blockSignals(true);
propertyAnim->stop();
QPointF endPos = propertyAnim->endValue().toPointF();
if (m_scrollOrientation == Qt::Vertical) {
endPos.ry() += diff;
} else {
endPos.rx() += diff;
}
propertyAnim->setDuration(remainingDuration);
propertyAnim->setStartValue(currentPos);
propertyAnim->setEndValue(endPos);
propertyAnim->start();
propertyAnim->blockSignals(block);
} else {
widget->setPos(currentPos);
}
}
}
}
qreal KItemListViewAnimation::offset() const
{
return m_offset;
}
void KItemListViewAnimation::start(QGraphicsWidget* widget, AnimationType type, const QVariant& endValue)
{
stop(widget, type);
const int duration = 200;
QPropertyAnimation* propertyAnim = 0;
switch (type) {
case MovingAnimation: {
const QPointF newPos = endValue.toPointF();
if (newPos == widget->pos()) {
return;
}
propertyAnim = new QPropertyAnimation(widget, "pos");
propertyAnim->setDuration(duration);
propertyAnim->setEndValue(newPos);
break;
}
case CreateAnimation: {
propertyAnim = new QPropertyAnimation(widget, "opacity");
propertyAnim->setEasingCurve(QEasingCurve::InQuart);
propertyAnim->setDuration(duration);
propertyAnim->setStartValue(0.0);
propertyAnim->setEndValue(1.0);
break;
}
case DeleteAnimation: {
propertyAnim = new QPropertyAnimation(widget, "opacity");
propertyAnim->setEasingCurve(QEasingCurve::OutQuart);
propertyAnim->setDuration(duration);
propertyAnim->setStartValue(1.0);
propertyAnim->setEndValue(0.0);
break;
}
case ResizeAnimation: {
const QSizeF newSize = endValue.toSizeF();
if (newSize == widget->size()) {
return;
}
propertyAnim = new QPropertyAnimation(widget, "size");
propertyAnim->setDuration(duration);
propertyAnim->setEndValue(newSize);
break;
}
default:
break;
}
Q_ASSERT(propertyAnim);
connect(propertyAnim, SIGNAL(finished()), this, SLOT(slotFinished()));
m_animation[type].insert(widget, propertyAnim);
propertyAnim->start();
}
void KItemListViewAnimation::stop(QGraphicsWidget* widget, AnimationType type)
{
QPropertyAnimation* propertyAnim = m_animation[type].value(widget);
if (propertyAnim) {
propertyAnim->stop();
switch (type) {
case MovingAnimation: break;
case CreateAnimation: widget->setOpacity(1.0); break;
case DeleteAnimation: widget->setOpacity(0.0); break;
case ResizeAnimation: break;
default: break;
}
m_animation[type].remove(widget);
delete propertyAnim;
emit finished(widget, type);
}
}
void KItemListViewAnimation::stop(QGraphicsWidget* widget)
{
for (int type = 0; type < AnimationTypeCount; ++type) {
stop(widget, static_cast<AnimationType>(type));
}
}
bool KItemListViewAnimation::isStarted(QGraphicsWidget *widget, AnimationType type) const
{
return m_animation[type].value(widget);
}
bool KItemListViewAnimation::isStarted(QGraphicsWidget* widget) const
{
for (int type = 0; type < AnimationTypeCount; ++type) {
if (isStarted(widget, static_cast<AnimationType>(type))) {
return true;
}
}
return false;
}
void KItemListViewAnimation::slotFinished()
{
QPropertyAnimation* finishedAnim = qobject_cast<QPropertyAnimation*>(sender());
for (int type = 0; type < AnimationTypeCount; ++type) {
QHashIterator<QGraphicsWidget*, QPropertyAnimation*> it(m_animation[type]);
while (it.hasNext()) {
it.next();
QPropertyAnimation* propertyAnim = it.value();
if (propertyAnim == finishedAnim) {
QGraphicsWidget* widget = it.key();
m_animation[type].remove(widget);
finishedAnim->deleteLater();
emit finished(widget, static_cast<AnimationType>(type));
return;
}
}
}
Q_ASSERT(false);
}
#include "kitemlistviewanimation_p.moc"

View file

@ -0,0 +1,79 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
***************************************************************************/
#ifndef KITEMLISTVIEWANIMATION_H
#define KITEMLISTVIEWANIMATION_H
#include <libdolphin_export.h>
#include <QHash>
#include <QObject>
#include <QVariant>
class KItemListView;
class QGraphicsWidget;
class QPointF;
class QPropertyAnimation;
class LIBDOLPHINPRIVATE_EXPORT KItemListViewAnimation : public QObject
{
Q_OBJECT
public:
enum AnimationType {
MovingAnimation,
CreateAnimation,
DeleteAnimation,
ResizeAnimation
};
KItemListViewAnimation(QObject* parent = 0);
virtual ~KItemListViewAnimation();
void setScrollOrientation(Qt::Orientation orientation);
Qt::Orientation scrollOrientation() const;
void setOffset(qreal offset);
qreal offset() const;
void start(QGraphicsWidget* widget, AnimationType type, const QVariant& endValue = QVariant());
void stop(QGraphicsWidget* widget, AnimationType type);
void stop(QGraphicsWidget* widget);
bool isStarted(QGraphicsWidget *widget, AnimationType type) const;
bool isStarted(QGraphicsWidget* widget) const;
signals:
void finished(QGraphicsWidget* widget, KItemListViewAnimation::AnimationType type);
private slots:
void slotFinished();
private:
enum { AnimationTypeCount = 4 };
Qt::Orientation m_scrollOrientation;
qreal m_offset;
QHash<QGraphicsWidget*, QPropertyAnimation*> m_animation[AnimationTypeCount];
};
#endif

View file

@ -0,0 +1,474 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 "kitemlistviewlayouter_p.h"
#include "kitemmodelbase.h"
#include "kitemlistsizehintresolver_p.h"
#include <KDebug>
#define KITEMLISTVIEWLAYOUTER_DEBUG
namespace {
// TODO
const int HeaderHeight = 50;
};
KItemListViewLayouter::KItemListViewLayouter(QObject* parent) :
QObject(parent),
m_dirty(true),
m_visibleIndexesDirty(true),
m_grouped(false),
m_scrollOrientation(Qt::Vertical),
m_size(),
m_itemSize(128, 128),
m_model(0),
m_sizeHintResolver(0),
m_offset(0),
m_maximumOffset(0),
m_firstVisibleIndex(-1),
m_lastVisibleIndex(-1),
m_firstVisibleGroupIndex(-1),
m_columnWidth(0),
m_xPosInc(0),
m_columnCount(0),
m_groups(),
m_groupIndexes(),
m_itemBoundingRects()
{
}
KItemListViewLayouter::~KItemListViewLayouter()
{
}
void KItemListViewLayouter::setScrollOrientation(Qt::Orientation orientation)
{
if (m_scrollOrientation != orientation) {
m_scrollOrientation = orientation;
m_dirty = true;
}
}
Qt::Orientation KItemListViewLayouter::scrollOrientation() const
{
return m_scrollOrientation;
}
void KItemListViewLayouter::setSize(const QSizeF& size)
{
if (m_size != size) {
m_size = size;
m_dirty = true;
}
}
QSizeF KItemListViewLayouter::size() const
{
return m_size;
}
void KItemListViewLayouter::setItemSize(const QSizeF& size)
{
if (m_itemSize != size) {
m_itemSize = size;
m_dirty = true;
}
}
QSizeF KItemListViewLayouter::itemSize() const
{
return m_itemSize;
}
void KItemListViewLayouter::setOffset(qreal offset)
{
if (m_offset != offset) {
m_offset = offset;
m_visibleIndexesDirty = true;
}
}
qreal KItemListViewLayouter::offset() const
{
return m_offset;
}
void KItemListViewLayouter::setModel(const KItemModelBase* model)
{
if (m_model != model) {
m_model = model;
m_dirty = true;
}
}
const KItemModelBase* KItemListViewLayouter::model() const
{
return m_model;
}
void KItemListViewLayouter::setSizeHintResolver(const KItemListSizeHintResolver* sizeHintResolver)
{
if (m_sizeHintResolver != sizeHintResolver) {
m_sizeHintResolver = sizeHintResolver;
m_dirty = true;
}
}
const KItemListSizeHintResolver* KItemListViewLayouter::sizeHintResolver() const
{
return m_sizeHintResolver;
}
qreal KItemListViewLayouter::maximumOffset() const
{
const_cast<KItemListViewLayouter*>(this)->doLayout();
return m_maximumOffset;
}
int KItemListViewLayouter::firstVisibleIndex() const
{
const_cast<KItemListViewLayouter*>(this)->doLayout();
return m_firstVisibleIndex;
}
int KItemListViewLayouter::lastVisibleIndex() const
{
const_cast<KItemListViewLayouter*>(this)->doLayout();
return m_lastVisibleIndex;
}
QRectF KItemListViewLayouter::itemBoundingRect(int index) const
{
const_cast<KItemListViewLayouter*>(this)->doLayout();
if (index < 0 || index >= m_itemBoundingRects.count()) {
return QRectF();
}
if (m_scrollOrientation == Qt::Horizontal) {
// Rotate the logical direction which is always vertical by 90°
// to get the physical horizontal direction
const QRectF& b = m_itemBoundingRects[index];
QRectF bounds(b.y(), b.x(), b.height(), b.width());
QPointF pos = bounds.topLeft();
pos.rx() -= m_offset;
bounds.moveTo(pos);
return bounds;
}
QRectF bounds = m_itemBoundingRects[index];
QPointF pos = bounds.topLeft();
pos.ry() -= m_offset;
bounds.moveTo(pos);
return bounds;
}
int KItemListViewLayouter::maximumVisibleItems() const
{
const_cast<KItemListViewLayouter*>(this)->doLayout();
const int height = static_cast<int>(m_size.height());
const int rowHeight = static_cast<int>(m_itemSize.height());
int rows = height / rowHeight;
if (height % rowHeight != 0) {
++rows;
}
return rows * m_columnCount;
}
bool KItemListViewLayouter::isFirstGroupItem(int itemIndex) const
{
return m_groupIndexes.contains(itemIndex);
}
void KItemListViewLayouter::markAsDirty()
{
m_dirty = true;
}
void KItemListViewLayouter::doLayout()
{
if (m_dirty) {
#ifdef KITEMLISTVIEWLAYOUTER_DEBUG
QElapsedTimer timer;
timer.start();
#endif
m_visibleIndexesDirty = true;
QSizeF itemSize = m_itemSize;
QSizeF size = m_size;
const bool horizontalScrolling = (m_scrollOrientation == Qt::Horizontal);
if (horizontalScrolling) {
itemSize.setWidth(m_itemSize.height());
itemSize.setHeight(m_itemSize.width());
size.setWidth(m_size.height());
size.setHeight(m_size.width());
}
m_columnWidth = itemSize.width();
m_columnCount = qMax(1, int(size.width() / m_columnWidth));
m_xPosInc = 0;
const int itemCount = m_model->count();
if (itemCount > m_columnCount) {
// Apply the unused width equally to each column
const qreal unusedWidth = size.width() - m_columnCount * m_columnWidth;
const qreal columnInc = unusedWidth / (m_columnCount + 1);
m_columnWidth += columnInc;
m_xPosInc += columnInc;
}
int rowCount = itemCount / m_columnCount;
if (itemCount % m_columnCount != 0) {
++rowCount;
}
m_itemBoundingRects.reserve(itemCount);
qreal y = 0;
int rowIndex = 0;
int index = 0;
while (index < itemCount) {
qreal x = m_xPosInc;
qreal maxItemHeight = itemSize.height();
int column = 0;
while (index < itemCount && column < m_columnCount) {
qreal requiredItemHeight = itemSize.height();
if (m_sizeHintResolver) {
const QSizeF sizeHint = m_sizeHintResolver->sizeHint(index);
const qreal sizeHintHeight = horizontalScrolling ? sizeHint.width() : sizeHint.height();
if (sizeHintHeight > requiredItemHeight) {
requiredItemHeight = sizeHintHeight;
}
}
const QRectF bounds(x, y, itemSize.width(), requiredItemHeight);
if (index < m_itemBoundingRects.count()) {
m_itemBoundingRects[index] = bounds;
} else {
m_itemBoundingRects.append(bounds);
}
maxItemHeight = qMax(maxItemHeight, requiredItemHeight);
x += m_columnWidth;
++index;
++column;
}
y += maxItemHeight;
++rowIndex;
}
if (m_itemBoundingRects.count() > itemCount) {
m_itemBoundingRects.erase(m_itemBoundingRects.begin() + itemCount,
m_itemBoundingRects.end());
}
m_maximumOffset = (itemCount > 0) ? m_itemBoundingRects.last().bottom() : 0;
m_grouped = !m_model->groupRole().isEmpty();
/*if (m_grouped) {
createGroupHeaders();
const int lastGroupItemCount = m_model->count() - m_groups.last().firstItemIndex;
m_maximumOffset = m_groups.last().y + (lastGroupItemCount / m_columnCount) * m_rowHeight;
if (lastGroupItemCount % m_columnCount != 0) {
m_maximumOffset += m_rowHeight;
}
} else {*/
// m_maximumOffset = m_minimumRowHeight * rowCount;
//}
#ifdef KITEMLISTVIEWLAYOUTER_DEBUG
kDebug() << "[TIME] doLayout() for " << m_model->count() << "items:" << timer.elapsed();
#endif
m_dirty = false;
}
if (m_grouped) {
updateGroupedVisibleIndexes();
} else {
updateVisibleIndexes();
}
}
void KItemListViewLayouter::updateVisibleIndexes()
{
if (!m_visibleIndexesDirty) {
return;
}
Q_ASSERT(!m_grouped);
Q_ASSERT(!m_dirty);
if (m_model->count() <= 0) {
m_firstVisibleIndex = -1;
m_lastVisibleIndex = -1;
m_visibleIndexesDirty = false;
return;
}
const bool horizontalScrolling = (m_scrollOrientation == Qt::Horizontal);
const int minimumHeight = horizontalScrolling ? m_itemSize.width()
: m_itemSize.height();
// Calculate the first visible index:
// 1. Guess the index by using the minimum row height
const int maxIndex = m_model->count() - 1;
m_firstVisibleIndex = int(m_offset / minimumHeight) * m_columnCount;
// 2. Decrease the index by checking the real row heights
int prevRowIndex = m_firstVisibleIndex - m_columnCount;
while (prevRowIndex > maxIndex) {
prevRowIndex -= m_columnCount;
}
while (prevRowIndex >= 0 && m_itemBoundingRects[prevRowIndex].bottom() >= m_offset) {
m_firstVisibleIndex = prevRowIndex;
prevRowIndex -= m_columnCount;
}
m_firstVisibleIndex = qBound(0, m_firstVisibleIndex, maxIndex);
// Calculate the last visible index
const int visibleHeight = horizontalScrolling ? m_size.width() : m_size.height();
const qreal bottom = m_offset + visibleHeight;
m_lastVisibleIndex = m_firstVisibleIndex; // first visible row, first column
int nextRowIndex = m_lastVisibleIndex + m_columnCount;
while (nextRowIndex <= maxIndex && m_itemBoundingRects[nextRowIndex].y() <= bottom) {
m_lastVisibleIndex = nextRowIndex;
nextRowIndex += m_columnCount;
}
m_lastVisibleIndex += m_columnCount - 1; // move it to the last column
m_lastVisibleIndex = qBound(0, m_lastVisibleIndex, maxIndex);
m_visibleIndexesDirty = false;
}
void KItemListViewLayouter::updateGroupedVisibleIndexes()
{
if (!m_visibleIndexesDirty) {
return;
}
Q_ASSERT(m_grouped);
Q_ASSERT(!m_dirty);
if (m_model->count() <= 0) {
m_firstVisibleIndex = -1;
m_lastVisibleIndex = -1;
m_visibleIndexesDirty = false;
return;
}
// Find the first visible group
const int lastGroupIndex = m_groups.count() - 1;
int groupIndex = lastGroupIndex;
for (int i = 1; i < m_groups.count(); ++i) {
if (m_groups[i].y >= m_offset) {
groupIndex = i - 1;
break;
}
}
// Calculate the first visible index
qreal groupY = m_groups[groupIndex].y;
m_firstVisibleIndex = m_groups[groupIndex].firstItemIndex;
const int invisibleRowCount = int(m_offset - groupY) / int(m_itemSize.height());
m_firstVisibleIndex += invisibleRowCount * m_columnCount;
if (groupIndex + 1 <= lastGroupIndex) {
// Check whether the calculated first visible index remains inside the current
// group. If this is not the case let the first element of the next group be the first
// visible index.
const int nextGroupIndex = m_groups[groupIndex + 1].firstItemIndex;
if (m_firstVisibleIndex > nextGroupIndex) {
m_firstVisibleIndex = nextGroupIndex;
}
}
m_firstVisibleGroupIndex = groupIndex;
const int maxIndex = m_model->count() - 1;
m_firstVisibleIndex = qBound(0, m_firstVisibleIndex, maxIndex);
// Calculate the last visible index: Find group where the last visible item is shown.
const qreal visibleBottom = m_offset + m_size.height(); // TODO: respect Qt::Horizontal alignment
while ((groupIndex < lastGroupIndex) && (m_groups[groupIndex + 1].y < visibleBottom)) {
++groupIndex;
}
groupY = m_groups[groupIndex].y;
m_lastVisibleIndex = m_groups[groupIndex].firstItemIndex;
const int availableHeight = static_cast<int>(visibleBottom - groupY);
int visibleRowCount = availableHeight / int(m_itemSize.height());
if (availableHeight % int(m_itemSize.height()) != 0) {
++visibleRowCount;
}
m_lastVisibleIndex += visibleRowCount * m_columnCount - 1;
if (groupIndex + 1 <= lastGroupIndex) {
// Check whether the calculate last visible index remains inside the current group.
// If this is not the case let the last element of this group be the last visible index.
const int nextGroupIndex = m_groups[groupIndex + 1].firstItemIndex;
if (m_lastVisibleIndex >= nextGroupIndex) {
m_lastVisibleIndex = nextGroupIndex - 1;
}
}
//Q_ASSERT(m_lastVisibleIndex < m_model->count());
m_lastVisibleIndex = qBound(0, m_lastVisibleIndex, maxIndex);
m_visibleIndexesDirty = false;
}
void KItemListViewLayouter::createGroupHeaders()
{
m_groups.clear();
m_groupIndexes.clear();
// TODO:
QList<int> numbers;
numbers << 0 << 5 << 6 << 13 << 20 << 25 << 30 << 35 << 50;
qreal y = 0;
for (int i = 0; i < numbers.count(); ++i) {
if (i > 0) {
const int previousGroupItemCount = numbers[i] - m_groups.last().firstItemIndex;
int previousGroupRowCount = previousGroupItemCount / m_columnCount;
if (previousGroupItemCount % m_columnCount != 0) {
++previousGroupRowCount;
}
const qreal previousGroupHeight = previousGroupRowCount * m_itemSize.height();
y += previousGroupHeight;
}
y += HeaderHeight;
ItemGroup itemGroup;
itemGroup.firstItemIndex = numbers[i];
itemGroup.y = y;
m_groups.append(itemGroup);
m_groupIndexes.insert(itemGroup.firstItemIndex);
}
}
#include "kitemlistviewlayouter_p.moc"

View file

@ -0,0 +1,126 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
***************************************************************************/
#ifndef KITEMLISTVIEWLAYOUTER_H
#define KITEMLISTVIEWLAYOUTER_H
#include <libdolphin_export.h>
#include <QObject>
#include <QRectF>
#include <QSet>
#include <QSizeF>
class KItemModelBase;
class KItemListSizeHintResolver;
class LIBDOLPHINPRIVATE_EXPORT KItemListViewLayouter : public QObject
{
Q_OBJECT
public:
KItemListViewLayouter(QObject* parent = 0);
virtual ~KItemListViewLayouter();
void setScrollOrientation(Qt::Orientation orientation);
Qt::Orientation scrollOrientation() const;
void setSize(const QSizeF& size);
QSizeF size() const;
void setItemSize(const QSizeF& size);
QSizeF itemSize() const;
// TODO: add note that offset can be < 0 or > maximumOffset!
void setOffset(qreal offset);
qreal offset() const;
void setModel(const KItemModelBase* model);
const KItemModelBase* model() const;
void setSizeHintResolver(const KItemListSizeHintResolver* sizeHintResolver);
const KItemListSizeHintResolver* sizeHintResolver() const;
qreal maximumOffset() const;
// TODO: mention that return value is -1 if count == 0
int firstVisibleIndex() const;
// TODO: mention that return value is -1 if count == 0
int lastVisibleIndex() const;
QRectF itemBoundingRect(int index) const;
int maximumVisibleItems() const;
/**
* @return True if the item with the index \p itemIndex
* is the first item within a group.
*/
bool isFirstGroupItem(int itemIndex) const;
void markAsDirty();
private:
void doLayout();
void updateVisibleIndexes();
void updateGroupedVisibleIndexes();
void createGroupHeaders();
private:
bool m_dirty;
bool m_visibleIndexesDirty;
bool m_grouped;
Qt::Orientation m_scrollOrientation;
QSizeF m_size;
QSizeF m_itemSize;
const KItemModelBase* m_model;
const KItemListSizeHintResolver* m_sizeHintResolver;
qreal m_offset;
qreal m_maximumOffset;
int m_firstVisibleIndex;
int m_lastVisibleIndex;
int m_firstVisibleGroupIndex;
qreal m_columnWidth;
qreal m_xPosInc;
int m_columnCount;
struct ItemGroup {
int firstItemIndex;
qreal y;
};
QList<ItemGroup> m_groups;
// Stores all item indexes that are the first item of a group.
// Assures fast access for KItemListViewLayouter::isFirstGroupItem().
QSet<int> m_groupIndexes;
QList<QRectF> m_itemBoundingRects;
};
#endif

View file

@ -0,0 +1,260 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* Based on the Itemviews NG project from Trolltech Labs: *
* http://qt.gitorious.org/qt-labs/itemviews-ng *
* *
* 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 "kitemlistwidget.h"
#include "kitemlistview.h"
#include "kitemmodelbase.h"
#include <KDebug>
#include <QPainter>
#include <QPropertyAnimation>
#include <QStyle>
KItemListWidget::KItemListWidget(QGraphicsItem* parent) :
QGraphicsWidget(parent, 0),
m_index(-1),
m_data(),
m_visibleRoles(),
m_visibleRolesSizes(),
m_styleOption(),
m_hoverOpacity(0),
m_hoverCache(0),
m_hoverAnimation(0)
{
}
KItemListWidget::~KItemListWidget()
{
clearCache();
}
void KItemListWidget::setIndex(int index)
{
if (m_index != index) {
if (m_hoverAnimation) {
m_hoverAnimation->stop();
m_hoverOpacity = 0;
}
clearCache();
m_index = index;
}
}
int KItemListWidget::index() const
{
return m_index;
}
void KItemListWidget::setData(const QHash<QByteArray, QVariant>& data,
const QSet<QByteArray>& roles)
{
clearCache();
if (roles.isEmpty()) {
m_data = data;
dataChanged(m_data);
} else {
foreach (const QByteArray& role, roles) {
m_data[role] = data[role];
}
dataChanged(m_data, roles);
}
}
QHash<QByteArray, QVariant> KItemListWidget::data() const
{
return m_data;
}
void KItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
Q_UNUSED(option);
if (m_hoverOpacity <= 0.0) {
return;
}
const QRect hoverBounds = hoverBoundingRect().toRect();
if (!m_hoverCache) {
m_hoverCache = new QPixmap(hoverBounds.size());
m_hoverCache->fill(Qt::transparent);
QPainter pixmapPainter(m_hoverCache);
QStyleOptionViewItemV4 viewItemOption;
viewItemOption.initFrom(widget);
viewItemOption.rect = QRect(0, 0, hoverBounds.width(), hoverBounds.height());
viewItemOption.state = QStyle::State_Enabled | QStyle::State_MouseOver;
viewItemOption.viewItemPosition = QStyleOptionViewItemV4::OnlyOne;
widget->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &viewItemOption, &pixmapPainter, widget);
}
const qreal opacity = painter->opacity();
painter->setOpacity(m_hoverOpacity * opacity);
painter->drawPixmap(hoverBounds.topLeft(), *m_hoverCache);
painter->setOpacity(opacity);
}
void KItemListWidget::setVisibleRoles(const QHash<QByteArray, int>& roles)
{
const QHash<QByteArray, int> previousRoles = m_visibleRoles;
m_visibleRoles = roles;
visibleRolesChanged(roles, previousRoles);
}
QHash<QByteArray, int> KItemListWidget::visibleRoles() const
{
return m_visibleRoles;
}
void KItemListWidget::setVisibleRolesSizes(const QHash<QByteArray, QSizeF> rolesSizes)
{
const QHash<QByteArray, QSizeF> previousRolesSizes = m_visibleRolesSizes;
m_visibleRolesSizes = rolesSizes;
visibleRolesSizesChanged(rolesSizes, previousRolesSizes);
}
QHash<QByteArray, QSizeF> KItemListWidget::visibleRolesSizes() const
{
return m_visibleRolesSizes;
}
void KItemListWidget::setStyleOption(const KItemListStyleOption& option)
{
const KItemListStyleOption previous = m_styleOption;
if (m_index >= 0) {
clearCache();
const bool wasHovered = (previous.state & QStyle::State_MouseOver);
m_styleOption = option;
const bool isHovered = (m_styleOption.state & QStyle::State_MouseOver);
if (wasHovered != isHovered) {
// The hovering state has been changed. Assure that a fade-animation
// is done to the new state.
if (!m_hoverAnimation) {
m_hoverAnimation = new QPropertyAnimation(this, "hoverOpacity", this);
m_hoverAnimation->setDuration(200);
}
m_hoverAnimation->stop();
if (!wasHovered && isHovered) {
m_hoverAnimation->setEndValue(1.0);
} else {
Q_ASSERT(wasHovered && !isHovered);
m_hoverAnimation->setEndValue(0.0);
}
m_hoverAnimation->start();
}
} else {
m_styleOption = option;
}
styleOptionChanged(option, previous);
}
const KItemListStyleOption& KItemListWidget::styleOption() const
{
return m_styleOption;
}
bool KItemListWidget::contains(const QPointF& point) const
{
return hoverBoundingRect().contains(point) ||
expansionToggleRect().contains(point) ||
selectionToggleRect().contains(point);
}
QRectF KItemListWidget::hoverBoundingRect() const
{
return QRectF(QPointF(0, 0), size());
}
QRectF KItemListWidget::selectionToggleRect() const
{
return QRectF();
}
QRectF KItemListWidget::expansionToggleRect() const
{
return QRectF();
}
void KItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& current,
const QSet<QByteArray>& roles)
{
Q_UNUSED(current);
Q_UNUSED(roles);
update();
}
void KItemListWidget::visibleRolesChanged(const QHash<QByteArray, int>& current,
const QHash<QByteArray, int>& previous)
{
Q_UNUSED(current);
Q_UNUSED(previous);
update();
}
void KItemListWidget::visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current,
const QHash<QByteArray, QSizeF>& previous)
{
Q_UNUSED(current);
Q_UNUSED(previous);
update();
}
void KItemListWidget::styleOptionChanged(const KItemListStyleOption& current,
const KItemListStyleOption& previous)
{
Q_UNUSED(current);
Q_UNUSED(previous);
update();
}
void KItemListWidget::resizeEvent(QGraphicsSceneResizeEvent* event)
{
QGraphicsWidget::resizeEvent(event);
clearCache();
}
qreal KItemListWidget::hoverOpacity() const
{
return m_hoverOpacity;
}
void KItemListWidget::setHoverOpacity(qreal opacity)
{
m_hoverOpacity = opacity;
update();
}
void KItemListWidget::clearCache()
{
delete m_hoverCache;
m_hoverCache = 0;
}
#include "kitemlistwidget.moc"

View file

@ -0,0 +1,134 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* Based on the Itemviews NG project from Trolltech Labs: *
* http://qt.gitorious.org/qt-labs/itemviews-ng *
* *
* 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 *
***************************************************************************/
#ifndef KITEMLISTWIDGET_H
#define KITEMLISTWIDGET_H
#include <libdolphin_export.h>
#include <kitemviews/kitemliststyleoption.h>
#include <QGraphicsWidget>
class QPropertyAnimation;
/**
* @brief Widget that shows a visible item from the model.
*
* For showing an item from a custom model it is required to at least overwrite KItemListWidget::paint().
* All properties are set by KItemListView, for each property there is a corresponding
* virtual protected method that allows to react on property changes.
*/
class LIBDOLPHINPRIVATE_EXPORT KItemListWidget : public QGraphicsWidget
{
Q_OBJECT
public:
KItemListWidget(QGraphicsItem* parent);
virtual ~KItemListWidget();
void setIndex(int index);
int index() const;
void setData(const QHash<QByteArray, QVariant>& data, const QSet<QByteArray>& roles = QSet<QByteArray>());
QHash<QByteArray, QVariant> data() const;
/**
* Draws the hover-bounding-rectangle if the item is hovered. Overwrite this method
* to show the data of the custom model provided by KItemListWidget::data().
* @reimp
*/
virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0);
/**
* Sets the visible roles to \p roles. The integer-value defines
* the order of the visible role: Smaller values are ordered first.
*/
void setVisibleRoles(const QHash<QByteArray, int>& roles);
QHash<QByteArray, int> visibleRoles() const;
void setVisibleRolesSizes(const QHash<QByteArray, QSizeF> rolesSizes);
QHash<QByteArray, QSizeF> visibleRolesSizes() const;
void setStyleOption(const KItemListStyleOption& option);
const KItemListStyleOption& styleOption() const;
/**
* @return True if \a point is inside KItemListWidget::hoverBoundingRect(),
* KItemListWidget::selectionToggleRect() or KItemListWidget::expansionToggleRect().
* @reimp
*/
virtual bool contains(const QPointF& point) const;
/**
* @return Bounding rectangle for the area that acts as hovering-area. Per default
* the bounding rectangle of the KItemListWidget is returned.
*/
virtual QRectF hoverBoundingRect() const;
/**
* @return Rectangle for the selection-toggle that is used to select or deselect an item.
* Per default an empty rectangle is returned which means that no selection-toggle
* is available.
*/
virtual QRectF selectionToggleRect() const;
/**
* @return Rectangle for the expansion-toggle that is used to open a sub-tree of the model.
* Per default an empty rectangle is returned which means that no opening of sub-trees
* is supported.
*/
virtual QRectF expansionToggleRect() const;
protected:
virtual void dataChanged(const QHash<QByteArray, QVariant>& current, const QSet<QByteArray>& roles = QSet<QByteArray>());
virtual void visibleRolesChanged(const QHash<QByteArray, int>& current, const QHash<QByteArray, int>& previous);
virtual void visibleRolesSizesChanged(const QHash<QByteArray, QSizeF>& current, const QHash<QByteArray, QSizeF>& previous);
virtual void styleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous);
virtual void resizeEvent(QGraphicsSceneResizeEvent* event);
/**
* @return The current opacity of the hover-animation. When implementing a custom painting-code for a hover-state
* this opacity value should be respected.
*/
qreal hoverOpacity() const;
private:
void setHoverOpacity(qreal opacity);
void clearCache();
private:
Q_PROPERTY(qreal hoverOpacity READ hoverOpacity WRITE setHoverOpacity)
int m_index;
QHash<QByteArray, QVariant> m_data;
QHash<QByteArray, int> m_visibleRoles;
QHash<QByteArray, QSizeF> m_visibleRolesSizes;
KItemListStyleOption m_styleOption;
qreal m_hoverOpacity;
mutable QPixmap* m_hoverCache;
QPropertyAnimation* m_hoverAnimation;
};
#endif

View file

@ -0,0 +1,113 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* Based on the Itemviews NG project from Trolltech Labs: *
* http://qt.gitorious.org/qt-labs/itemviews-ng *
* *
* 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 "kitemmodelbase.h"
KItemRange::KItemRange(int index, int count) :
index(index),
count(count)
{
}
KItemModelBase::KItemModelBase(QObject* parent) :
QObject(parent),
m_groupRole(),
m_sortRole()
{
}
KItemModelBase::KItemModelBase(const QByteArray& groupRole, const QByteArray& sortRole, QObject* parent) :
QObject(parent),
m_groupRole(groupRole),
m_sortRole(sortRole)
{
}
KItemModelBase::~KItemModelBase()
{
}
bool KItemModelBase::setData(int index, const QHash<QByteArray, QVariant> &values)
{
Q_UNUSED(index);
Q_UNUSED(values);
return false;
}
bool KItemModelBase::supportsGrouping() const
{
return false;
}
void KItemModelBase::setGroupRole(const QByteArray& role)
{
if (supportsGrouping() && role != m_groupRole) {
const QByteArray previous = m_groupRole;
m_groupRole = role;
onGroupRoleChanged(role, previous);
emit groupRoleChanged(role, previous);
}
}
QByteArray KItemModelBase::groupRole() const
{
return m_groupRole;
}
bool KItemModelBase::supportsSorting() const
{
return false;
}
void KItemModelBase::setSortRole(const QByteArray& role)
{
if (supportsSorting() && role != m_sortRole) {
const QByteArray previous = m_sortRole;
m_sortRole = role;
onSortRoleChanged(role, previous);
emit sortRoleChanged(role, previous);
}
}
QByteArray KItemModelBase::sortRole() const
{
return m_sortRole;
}
QString KItemModelBase::roleDescription(const QByteArray& role) const
{
return role;
}
void KItemModelBase::onGroupRoleChanged(const QByteArray& current, const QByteArray& previous)
{
Q_UNUSED(current);
Q_UNUSED(previous);
}
void KItemModelBase::onSortRoleChanged(const QByteArray& current, const QByteArray& previous)
{
Q_UNUSED(current);
Q_UNUSED(previous);
}
#include "kitemmodelbase.moc"

View file

@ -0,0 +1,145 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* Based on the Itemviews NG project from Trolltech Labs: *
* http://qt.gitorious.org/qt-labs/itemviews-ng *
* *
* 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 *
***************************************************************************/
#ifndef KITEMMODELBASE_H
#define KITEMMODELBASE_H
#include <libdolphin_export.h>
#include <QHash>
#include <QObject>
#include <QSet>
#include <QVariant>
struct KItemRange
{
KItemRange(int index, int count);
int index;
int count;
};
typedef QList<KItemRange> KItemRangeList;
/**
* @brief Base class for model implementations used by KItemListView and KItemListController.
*
* A item-model consists of a variable number of items. The number of items
* is given by KItemModelBase::count(). The data of an item is accessed by a unique index
* with KItemModelBase::data(). The indexes are integer-values counting from 0 to the
* KItemModelBase::count() - 1.
*
* One item consists of a variable number of role/value-pairs.
*
* A model can optionally provide sorting- and/or grouping-capabilities.
*/
class LIBDOLPHINPRIVATE_EXPORT KItemModelBase : public QObject
{
Q_OBJECT
public:
KItemModelBase(QObject* parent = 0);
KItemModelBase(const QByteArray& groupRole, const QByteArray& sortRole, QObject* parent = 0);
virtual ~KItemModelBase();
/** @return The number of items. */
virtual int count() const = 0;
virtual QHash<QByteArray, QVariant> data(int index) const = 0;
/**
* Sets the data for the item at \a index to the given \a values. Returns true
* if the data was set on the item; returns false otherwise.
*
* The default implementation does not set the data, and will always return
* false.
*/
virtual bool setData(int index, const QHash<QByteArray, QVariant>& values);
/**
* @return True if the model supports grouping of data. Per default false is returned.
* If the model should support grouping it is necessary to overwrite
* this method to return true and to implement KItemModelBase::onGroupRoleChanged().
*/
virtual bool supportsGrouping() const;
/**
* Sets the group-role to \a role. The method KItemModelBase::onGroupRoleChanged() will be
* called so that model-implementations can react on the group-role change. Afterwards the
* signal groupRoleChanged() will be emitted.
*/
void setGroupRole(const QByteArray& role);
QByteArray groupRole() const;
/**
* @return True if the model supports sorting of data. Per default false is returned.
* If the model should support sorting it is necessary to overwrite
* this method to return true and to implement KItemModelBase::onSortRoleChanged().
*/
virtual bool supportsSorting() const;
/**
* Sets the sor-role to \a role. The method KItemModelBase::onSortRoleChanged() will be
* called so that model-implementations can react on the sort-role change. Afterwards the
* signal sortRoleChanged() will be emitted.
*/
void setSortRole(const QByteArray& role);
QByteArray sortRole() const;
virtual QString roleDescription(const QByteArray& role) const;
signals:
void itemsInserted(const KItemRangeList& itemRanges);
void itemsRemoved(const KItemRangeList& itemRanges);
void itemsMoved(const KItemRangeList& itemRanges);
void itemsChanged(const KItemRangeList& itemRanges, const QSet<QByteArray>& roles);
void groupRoleChanged(const QByteArray& current, const QByteArray& previous);
void sortRoleChanged(const QByteArray& current, const QByteArray& previous);
protected:
/**
* Is invoked if the group role has been changed by KItemModelBase::setGroupRole(). Allows
* to react on the changed group role before the signal groupRoleChanged() will be emitted.
* The implementation must assure that the items are sorted in a way that they are grouped
* by the role given by \a current. Usually the most efficient way is to emit a
* itemsRemoved() signal for all items, reorder the items internally and to emit a
* itemsInserted() signal afterwards.
*/
virtual void onGroupRoleChanged(const QByteArray& current, const QByteArray& previous);
/**
* Is invoked if the sort role has been changed by KItemModelBase::setSortRole(). Allows
* to react on the changed sort role before the signal sortRoleChanged() will be emitted.
* The implementation must assure that the items are sorted by the role given by \a current.
* Usually the most efficient way is to emit a
* itemsRemoved() signal for all items, reorder the items internally and to emit a
* itemsInserted() signal afterwards.
*/
virtual void onSortRoleChanged(const QByteArray& current, const QByteArray& previous);
private:
QByteArray m_groupRole;
QByteArray m_sortRole;
};
#endif

View file

@ -0,0 +1,388 @@
//krazy:exclude=copyright (email of Maxim is missing)
/*
This file is a part of the KDE project
Copyright © 2006 Zack Rusin <zack@kde.org>
Copyright © 2006-2007, 2008 Fredrik Höglund <fredrik@kde.org>
The stack blur algorithm was invented by Mario Klingemann <mario@quasimondo.com>
This implementation is based on the version in Anti-Grain Geometry Version 2.4,
Copyright © 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "kpixmapmodifier_p.h"
#include <QImage>
#include <QPainter>
#include <QPixmap>
#include <QSize>
#include <KDebug>
#include <config-X11.h> // for HAVE_XRENDER
#if defined(Q_WS_X11) && defined(HAVE_XRENDER)
# include <QX11Info>
# include <X11/Xlib.h>
# include <X11/extensions/Xrender.h>
#endif
static const quint32 stackBlur8Mul[255] =
{
512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,
454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,
482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,
437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,
497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,
320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,
446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,
329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,
505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,
399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,
324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,
268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,
451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,
385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,
332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,
289,287,285,282,280,278,275,273,271,269,267,265,263,261,259
};
static const quint32 stackBlur8Shr[255] =
{
9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
};
static void blurHorizontal(QImage& image, unsigned int* stack, int div, int radius)
{
int stackindex;
int stackstart;
quint32 * const pixels = reinterpret_cast<quint32 *>(image.bits());
quint32 pixel;
int w = image.width();
int h = image.height();
int wm = w - 1;
unsigned int mulSum = stackBlur8Mul[radius];
unsigned int shrSum = stackBlur8Shr[radius];
unsigned int sum, sumIn, sumOut;
for (int y = 0; y < h; y++) {
sum = 0;
sumIn = 0;
sumOut = 0;
const int yw = y * w;
pixel = pixels[yw];
for (int i = 0; i <= radius; i++) {
stack[i] = qAlpha(pixel);
sum += stack[i] * (i + 1);
sumOut += stack[i];
}
for (int i = 1; i <= radius; i++) {
pixel = pixels[yw + qMin(i, wm)];
unsigned int* stackpix = &stack[i + radius];
*stackpix = qAlpha(pixel);
sum += *stackpix * (radius + 1 - i);
sumIn += *stackpix;
}
stackindex = radius;
for (int x = 0, i = yw; x < w; x++) {
pixels[i++] = (((sum * mulSum) >> shrSum) << 24) & 0xff000000;
sum -= sumOut;
stackstart = stackindex + div - radius;
if (stackstart >= div) {
stackstart -= div;
}
unsigned int* stackpix = &stack[stackstart];
sumOut -= *stackpix;
pixel = pixels[yw + qMin(x + radius + 1, wm)];
*stackpix = qAlpha(pixel);
sumIn += *stackpix;
sum += sumIn;
if (++stackindex >= div) {
stackindex = 0;
}
stackpix = &stack[stackindex];
sumOut += *stackpix;
sumIn -= *stackpix;
}
}
}
static void blurVertical(QImage& image, unsigned int* stack, int div, int radius)
{
int stackindex;
int stackstart;
quint32 * const pixels = reinterpret_cast<quint32 *>(image.bits());
quint32 pixel;
int w = image.width();
int h = image.height();
int hm = h - 1;
int mul_sum = stackBlur8Mul[radius];
int shr_sum = stackBlur8Shr[radius];
unsigned int sum, sumIn, sumOut;
for (int x = 0; x < w; x++) {
sum = 0;
sumIn = 0;
sumOut = 0;
pixel = pixels[x];
for (int i = 0; i <= radius; i++) {
stack[i] = qAlpha(pixel);
sum += stack[i] * (i + 1);
sumOut += stack[i];
}
for (int i = 1; i <= radius; i++) {
pixel = pixels[qMin(i, hm) * w + x];
unsigned int* stackpix = &stack[i + radius];
*stackpix = qAlpha(pixel);
sum += *stackpix * (radius + 1 - i);
sumIn += *stackpix;
}
stackindex = radius;
for (int y = 0, i = x; y < h; y++, i += w) {
pixels[i] = (((sum * mul_sum) >> shr_sum) << 24) & 0xff000000;
sum -= sumOut;
stackstart = stackindex + div - radius;
if (stackstart >= div)
stackstart -= div;
unsigned int* stackpix = &stack[stackstart];
sumOut -= *stackpix;
pixel = pixels[qMin(y + radius + 1, hm) * w + x];
*stackpix = qAlpha(pixel);
sumIn += *stackpix;
sum += sumIn;
if (++stackindex >= div) {
stackindex = 0;
}
stackpix = &stack[stackindex];
sumOut += *stackpix;
sumIn -= *stackpix;
}
}
}
static void stackBlur(QImage& image, float radius)
{
radius = qRound(radius);
int div = int(radius * 2) + 1;
unsigned int* stack = new unsigned int[div];
blurHorizontal(image, stack, div, radius);
blurVertical(image, stack, div, radius);
delete [] stack;
}
static void shadowBlur(QImage& image, float radius, const QColor& color)
{
if (radius < 0) {
return;
}
if (radius > 0) {
stackBlur(image, radius);
}
// Correct the color and opacity of the shadow
QPainter p(&image);
p.setCompositionMode(QPainter::CompositionMode_SourceIn);
p.fillRect(image.rect(), color);
}
namespace {
/** Helper class for drawing frames for KPixmapModifier::applyFrame(). */
class TileSet
{
public:
enum { LeftMargin = 3, TopMargin = 2, RightMargin = 3, BottomMargin = 4 };
enum Tile { TopLeftCorner = 0, TopSide, TopRightCorner, LeftSide,
RightSide, BottomLeftCorner, BottomSide, BottomRightCorner,
NumTiles };
TileSet()
{
QImage image(8 * 3, 8 * 3, QImage::Format_ARGB32_Premultiplied);
QPainter p(&image);
p.setCompositionMode(QPainter::CompositionMode_Source);
p.fillRect(image.rect(), Qt::transparent);
p.fillRect(image.rect().adjusted(3, 3, -3, -3), Qt::black);
p.end();
shadowBlur(image, 3, Qt::black);
QPixmap pixmap = QPixmap::fromImage(image);
m_tiles[TopLeftCorner] = pixmap.copy(0, 0, 8, 8);
m_tiles[TopSide] = pixmap.copy(8, 0, 8, 8);
m_tiles[TopRightCorner] = pixmap.copy(16, 0, 8, 8);
m_tiles[LeftSide] = pixmap.copy(0, 8, 8, 8);
m_tiles[RightSide] = pixmap.copy(16, 8, 8, 8);
m_tiles[BottomLeftCorner] = pixmap.copy(0, 16, 8, 8);
m_tiles[BottomSide] = pixmap.copy(8, 16, 8, 8);
m_tiles[BottomRightCorner] = pixmap.copy(16, 16, 8, 8);
}
void paint(QPainter* p, const QRect& r)
{
p->drawPixmap(r.topLeft(), m_tiles[TopLeftCorner]);
if (r.width() - 16 > 0) {
p->drawTiledPixmap(r.x() + 8, r.y(), r.width() - 16, 8, m_tiles[TopSide]);
}
p->drawPixmap(r.right() - 8 + 1, r.y(), m_tiles[TopRightCorner]);
if (r.height() - 16 > 0) {
p->drawTiledPixmap(r.x(), r.y() + 8, 8, r.height() - 16, m_tiles[LeftSide]);
p->drawTiledPixmap(r.right() - 8 + 1, r.y() + 8, 8, r.height() - 16, m_tiles[RightSide]);
}
p->drawPixmap(r.x(), r.bottom() - 8 + 1, m_tiles[BottomLeftCorner]);
if (r.width() - 16 > 0) {
p->drawTiledPixmap(r.x() + 8, r.bottom() - 8 + 1, r.width() - 16, 8, m_tiles[BottomSide]);
}
p->drawPixmap(r.right() - 8 + 1, r.bottom() - 8 + 1, m_tiles[BottomRightCorner]);
const QRect contentRect = r.adjusted(LeftMargin + 1, TopMargin + 1,
-(RightMargin + 1), -(BottomMargin + 1));
p->fillRect(contentRect, Qt::transparent);
}
QPixmap m_tiles[NumTiles];
};
}
void KPixmapModifier::scale(QPixmap& pixmap, const QSize& scaledSize)
{
#if defined(Q_WS_X11) && defined(HAVE_XRENDER)
// Assume that the texture size limit is 2048x2048
if ((pixmap.width() <= 2048) && (pixmap.height() <= 2048) && pixmap.x11PictureHandle()) {
QSize scaledPixmapSize = pixmap.size();
scaledPixmapSize.scale(scaledSize, Qt::KeepAspectRatio);
const qreal factor = scaledPixmapSize.width() / qreal(pixmap.width());
XTransform xform = {{
{ XDoubleToFixed(1 / factor), 0, 0 },
{ 0, XDoubleToFixed(1 / factor), 0 },
{ 0, 0, XDoubleToFixed(1) }
}};
QPixmap scaledPixmap(scaledPixmapSize);
scaledPixmap.fill(Qt::transparent);
Display* dpy = QX11Info::display();
XRenderPictureAttributes attr;
attr.repeat = RepeatPad;
XRenderChangePicture(dpy, pixmap.x11PictureHandle(), CPRepeat, &attr);
XRenderSetPictureFilter(dpy, pixmap.x11PictureHandle(), FilterBilinear, 0, 0);
XRenderSetPictureTransform(dpy, pixmap.x11PictureHandle(), &xform);
XRenderComposite(dpy, PictOpOver, pixmap.x11PictureHandle(), None, scaledPixmap.x11PictureHandle(),
0, 0, 0, 0, 0, 0, scaledPixmap.width(), scaledPixmap.height());
pixmap = scaledPixmap;
} else {
pixmap = pixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::FastTransformation);
}
#else
pixmap = pixmap.scaled(scaledSize, Qt::KeepAspectRatio, Qt::FastTransformation);
#endif
}
void KPixmapModifier::applyFrame(QPixmap& icon, const QSize& scaledSize)
{
static TileSet tileSet;
// Resize the icon to the maximum size minus the space required for the frame
const QSize size(scaledSize.width() - TileSet::LeftMargin - TileSet::RightMargin,
scaledSize.height() - TileSet::TopMargin - TileSet::BottomMargin);
scale(icon, size);
QPixmap framedIcon(icon.size().width() + TileSet::LeftMargin + TileSet::RightMargin,
icon.size().height() + TileSet::TopMargin + TileSet::BottomMargin);
framedIcon.fill(Qt::transparent);
QPainter painter;
painter.begin(&framedIcon);
painter.setCompositionMode(QPainter::CompositionMode_Source);
tileSet.paint(&painter, framedIcon.rect());
painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
painter.drawPixmap(TileSet::LeftMargin, TileSet::TopMargin, icon);
icon = framedIcon;
}

View file

@ -1,5 +1,5 @@
/***************************************************************************
* Copyright (C) 2010 by Frank Reininghaus (frank78ac@googlemail.com) *
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
@ -17,26 +17,21 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
***************************************************************************/
#include <qtest_kde.h>
#ifndef KPIXMAPMODIFIER_H
#define KPIXMAPMODIFIER_H
#include "dolphinviewtest_allviewmodes.h"
#include <libdolphin_export.h>
class DolphinViewTest_Icons : public DolphinViewTest_AllViewModes
class QPixmap;
class QSize;
class LIBDOLPHINPRIVATE_EXPORT KPixmapModifier
{
Q_OBJECT
public:
virtual DolphinView::Mode mode() const {
return DolphinView::IconsView;
}
virtual bool verifyCorrectViewMode(const DolphinView* view) const {
return (view->mode() == DolphinView::IconsView);
}
static void scale(QPixmap& pixmap, const QSize& scaledSize);
static void applyFrame(QPixmap& icon, const QSize& scaledSize);
};
QTEST_KDEMAIN(DolphinViewTest_Icons, GUI)
#endif
#include "dolphinviewtest_icons.moc"

View file

@ -33,7 +33,7 @@ KDE_EXPORT int kdemain(int argc, char **argv)
{
KAboutData about("dolphin", 0,
ki18nc("@title", "Dolphin"),
"1.6.9",
"1.99",
ki18nc("@title", "File Manager"),
KAboutData::License_GPL,
ki18nc("@info:credit", "(C) 2006-2011 Peter Penz"));

View file

@ -6,8 +6,8 @@
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd">
<kcfgfile name="dolphinrc"/>
<group name="FoldersPanel">
<entry name="ShowHiddenFiles" type="Bool">
<label>Show hidden files</label>
<entry name="HiddenFilesShown" type="Bool">
<label>Hidden files shown</label>
<default>false</default>
</entry>
<entry name="AutoScrolling" type="Bool">

View file

@ -39,9 +39,6 @@
#include <QScrollBar>
#include <QTimer>
#include <views/draganddrophelper.h>
#include <views/dolphinmodel.h>
#include <views/dolphinsortfilterproxymodel.h>
#include <views/dolphinview.h>
#include <views/folderexpander.h>
#include <views/renamedialog.h>
@ -51,8 +48,8 @@ FoldersPanel::FoldersPanel(QWidget* parent) :
m_setLeafVisible(false),
m_mouseButtons(Qt::NoButton),
m_dirLister(0),
m_dolphinModel(0),
m_proxyModel(0),
//m_dolphinModel(0),
//m_proxyModel(0),
m_treeView(0),
m_leafDir()
{
@ -63,25 +60,26 @@ FoldersPanel::~FoldersPanel()
{
FoldersPanelSettings::self()->writeConfig();
delete m_proxyModel;
m_proxyModel = 0;
delete m_dolphinModel;
m_dolphinModel = 0;
m_dirLister = 0; // deleted by m_dolphinModel
//delete m_proxyModel;
//m_proxyModel = 0;
//delete m_dolphinModel;
//m_dolphinModel = 0;
delete m_dirLister;
m_dirLister = 0;
}
void FoldersPanel::setShowHiddenFiles(bool show)
void FoldersPanel::setHiddenFilesShown(bool show)
{
FoldersPanelSettings::setShowHiddenFiles(show);
FoldersPanelSettings::setHiddenFilesShown(show);
if (m_dirLister) {
m_dirLister->setShowingDotFiles(show);
m_dirLister->openUrl(m_dirLister->url(), KDirLister::Reload);
}
}
bool FoldersPanel::showHiddenFiles() const
bool FoldersPanel::hiddenFilesShown() const
{
return FoldersPanelSettings::showHiddenFiles();
return FoldersPanelSettings::hiddenFilesShown();
}
void FoldersPanel::setAutoScrolling(bool enable)
@ -98,9 +96,9 @@ bool FoldersPanel::autoScrolling() const
void FoldersPanel::rename(const KFileItem& item)
{
if (DolphinSettings::instance().generalSettings()->renameInline()) {
const QModelIndex dirIndex = m_dolphinModel->indexForItem(item);
const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
m_treeView->edit(proxyIndex);
//const QModelIndex dirIndex = m_dolphinModel->indexForItem(item);
//const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
//m_treeView->edit(proxyIndex);
} else {
RenameDialog* dialog = new RenameDialog(this, KFileItemList() << item);
dialog->setAttribute(Qt::WA_DeleteOnClose);
@ -143,10 +141,10 @@ void FoldersPanel::showEvent(QShowEvent* event)
m_dirLister->setMainWindow(window());
m_dirLister->setDelayedMimeTypes(true);
m_dirLister->setAutoErrorHandlingEnabled(false, this);
m_dirLister->setShowingDotFiles(FoldersPanelSettings::showHiddenFiles());
m_dirLister->setShowingDotFiles(FoldersPanelSettings::hiddenFilesShown());
connect(m_dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted()));
Q_ASSERT(!m_dolphinModel);
/*Q_ASSERT(!m_dolphinModel);
m_dolphinModel = new DolphinModel(this);
m_dolphinModel->setDirLister(m_dirLister);
m_dolphinModel->setDropsAllowed(DolphinModel::DropOnDirectory);
@ -180,7 +178,7 @@ void FoldersPanel::showEvent(QShowEvent* event)
QVBoxLayout* layout = new QVBoxLayout(this);
layout->setMargin(0);
layout->addWidget(m_treeView);
layout->addWidget(m_treeView);*/
}
loadTree(url());
@ -192,11 +190,11 @@ void FoldersPanel::contextMenuEvent(QContextMenuEvent* event)
Panel::contextMenuEvent(event);
KFileItem item;
const QModelIndex index = m_treeView->indexAt(event->pos());
/*const QModelIndex index = m_treeView->indexAt(event->pos());
if (index.isValid()) {
const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
item = m_dolphinModel->itemForIndex(dolphinModelIndex);
}
}*/
QPointer<TreeViewContextMenu> contextMenu = new TreeViewContextMenu(this, item);
contextMenu->open();
@ -216,22 +214,25 @@ void FoldersPanel::keyPressEvent(QKeyEvent* event)
void FoldersPanel::updateActiveView(const QModelIndex& index)
{
const QModelIndex dirIndex = m_proxyModel->mapToSource(index);
Q_UNUSED(index);
/*const QModelIndex dirIndex = m_proxyModel->mapToSource(index);
const KFileItem item = m_dolphinModel->itemForIndex(dirIndex);
if (!item.isNull()) {
emit changeUrl(item.url(), m_mouseButtons);
}
}*/
}
void FoldersPanel::dropUrls(const QModelIndex& index, QDropEvent* event)
{
Q_UNUSED(event);
if (index.isValid()) {
const QModelIndex dirIndex = m_proxyModel->mapToSource(index);
/*const QModelIndex dirIndex = m_proxyModel->mapToSource(index);
KFileItem item = m_dolphinModel->itemForIndex(dirIndex);
Q_ASSERT(!item.isNull());
if (item.isDir()) {
DragAndDropHelper::instance().dropUrls(item, item.url(), event, this);
}
Q_UNUSED(event);
//DragAndDropHelper::instance().dropUrls(item, item.url(), event, this);
}*/
}
}
@ -243,11 +244,11 @@ void FoldersPanel::expandToDir(const QModelIndex& index)
void FoldersPanel::scrollToLeaf()
{
const QModelIndex dirIndex = m_dolphinModel->indexForUrl(m_leafDir);
/*const QModelIndex dirIndex = m_dolphinModel->indexForUrl(m_leafDir);
const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
if (proxyIndex.isValid()) {
m_treeView->scrollTo(proxyIndex);
}
}*/
}
void FoldersPanel::updateMouseButtons()
@ -257,7 +258,7 @@ void FoldersPanel::updateMouseButtons()
void FoldersPanel::slotDirListerCompleted()
{
m_treeView->resizeColumnToContents(DolphinModel::Name);
// m_treeView->resizeColumnToContents(DolphinModel::Name);
}
void FoldersPanel::slotHorizontalScrollBarMoved(int value)
@ -295,12 +296,12 @@ void FoldersPanel::loadTree(const KUrl& url)
m_dirLister->stop();
m_dirLister->openUrl(baseUrl, KDirLister::Reload);
}
m_dolphinModel->expandToUrl(m_leafDir);
//m_dolphinModel->expandToUrl(m_leafDir);
}
void FoldersPanel::selectLeafDirectory()
{
const QModelIndex dirIndex = m_dolphinModel->indexForUrl(m_leafDir);
/*const QModelIndex dirIndex = m_dolphinModel->indexForUrl(m_leafDir);
const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
if (proxyIndex.isValid()) {
@ -314,7 +315,7 @@ void FoldersPanel::selectLeafDirectory()
QTimer::singleShot(0, this, SLOT(scrollToLeaf()));
m_setLeafVisible = false;
}
}
}*/
}
#include "folderspanel.moc"

View file

@ -45,8 +45,8 @@ public:
FoldersPanel(QWidget* parent = 0);
virtual ~FoldersPanel();
void setShowHiddenFiles(bool show);
bool showHiddenFiles() const;
void setHiddenFilesShown(bool show);
bool hiddenFilesShown() const;
void setAutoScrolling(bool enable);
bool autoScrolling() const;
@ -123,8 +123,8 @@ private:
bool m_setLeafVisible;
Qt::MouseButtons m_mouseButtons;
KDirLister* m_dirLister;
DolphinModel* m_dolphinModel;
DolphinSortFilterProxyModel* m_proxyModel;
//DolphinModel* m_dolphinModel;
//DolphinSortFilterProxyModel* m_proxyModel;
PanelTreeView* m_treeView;
KUrl m_leafDir;
};

View file

@ -26,9 +26,6 @@
#include <QHeaderView>
#include <QScrollBar>
#include <views/dolphinmodel.h>
#include <views/draganddrophelper.h>
PanelTreeView::PanelTreeView(QWidget* parent) :
KTreeView(parent)
{
@ -69,10 +66,10 @@ bool PanelTreeView::event(QEvent* event)
switch (event->type()) {
case QEvent::Polish:
// Hide all columns except of the 'Name' column
for (int i = DolphinModel::Name + 1; i < DolphinModel::ExtraColumnCount; ++i) {
/*for (int i = DolphinModel::Name + 1; i < DolphinModel::ExtraColumnCount; ++i) {
hideColumn(i);
}
header()->hide();
header()->hide();*/
break;
case QEvent::Show:
@ -97,7 +94,8 @@ bool PanelTreeView::event(QEvent* event)
void PanelTreeView::startDrag(Qt::DropActions supportedActions)
{
DragAndDropHelper::instance().startDrag(this, supportedActions);
Q_UNUSED(supportedActions);
//DragAndDropHelper::instance().startDrag(this, supportedActions);
}
void PanelTreeView::dragEnterEvent(QDragEnterEvent* event)

View file

@ -117,7 +117,7 @@ void TreeViewContextMenu::open()
QAction* showHiddenFilesAction = new QAction(i18nc("@action:inmenu", "Show Hidden Files"), this);
showHiddenFilesAction->setCheckable(true);
showHiddenFilesAction->setChecked(m_parent->showHiddenFiles());
showHiddenFilesAction->setChecked(m_parent->hiddenFilesShown());
popup->addAction(showHiddenFilesAction);
connect(showHiddenFilesAction, SIGNAL(toggled(bool)), this, SLOT(setShowHiddenFiles(bool)));
@ -199,7 +199,7 @@ void TreeViewContextMenu::showProperties()
void TreeViewContextMenu::setShowHiddenFiles(bool show)
{
m_parent->setShowHiddenFiles(show);
m_parent->setHiddenFilesShown(show);
}
void TreeViewContextMenu::setAutoScrolling(bool enable)

View file

@ -6,8 +6,8 @@
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd">
<kcfgfile name="dolphinrc"/>
<group name="InformationPanel">
<entry name="showPreview" type="Bool">
<label>Show preview</label>
<entry name="previewsShown" type="Bool">
<label>Previews shown</label>
<default>true</default>
</entry>
</group>

View file

@ -102,8 +102,8 @@ InformationPanelContent::InformationPanelContent(QWidget* parent) :
m_nameLabel->setAlignment(Qt::AlignHCenter);
m_nameLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
const bool showPreview = InformationPanelSettings::showPreview();
m_preview->setVisible(showPreview);
const bool previewsShown = InformationPanelSettings::previewsShown();
m_preview->setVisible(previewsShown);
m_metaDataWidget = new KFileMetaDataWidget(parent);
m_metaDataWidget->setFont(KGlobalSettings::smallestReadableFont());
@ -188,7 +188,7 @@ void InformationPanelContent::showItem(const KFileItem& item)
m_metaDataWidget->setItems(KFileItemList() << item);
}
if (InformationPanelSettings::showPreview()) {
if (InformationPanelSettings::previewsShown()) {
const QString mimeType = item.mimetype();
const bool usePhonon = Phonon::BackendCapabilities::isMimeTypeAvailable(mimeType) &&
(mimeType != "image/png"); // TODO: workaround, as Phonon
@ -267,7 +267,7 @@ void InformationPanelContent::configureSettings(const QList<QAction*>& customCon
QAction* previewAction = popup.addAction(i18nc("@action:inmenu", "Preview"));
previewAction->setIcon(KIcon("view-preview"));
previewAction->setCheckable(true);
previewAction->setChecked(InformationPanelSettings::showPreview());
previewAction->setChecked(InformationPanelSettings::previewsShown());
QAction* configureAction = popup.addAction(i18nc("@action:inmenu", "Configure..."));
configureAction->setIcon(KIcon("configure"));
@ -287,7 +287,7 @@ void InformationPanelContent::configureSettings(const QList<QAction*>& customCon
const bool isChecked = action->isChecked();
if (action == previewAction) {
m_preview->setVisible(isChecked);
InformationPanelSettings::setShowPreview(isChecked);
InformationPanelSettings::setPreviewsShown(isChecked);
} else if (action == configureAction) {
FileMetaDataConfigurationDialog* dialog = new FileMetaDataConfigurationDialog();
dialog->setDescription(i18nc("@label::textbox",

View file

@ -22,7 +22,6 @@
#include <KFileItem>
#include <konq_operations.h>
#include "views/draganddrophelper.h"
PlacesPanel::PlacesPanel(QWidget* parent) :
KFilePlacesView(parent),
@ -47,7 +46,10 @@ void PlacesPanel::mousePressEvent(QMouseEvent* event)
void PlacesPanel::slotUrlsDropped(const KUrl& dest, QDropEvent* event, QWidget* parent)
{
DragAndDropHelper::instance().dropUrls(KFileItem(), dest, event, parent);
Q_UNUSED(dest);
Q_UNUSED(event);
Q_UNUSED(parent);
//DragAndDropHelper::instance().dropUrls(KFileItem(), dest, event, parent);
}
void PlacesPanel::emitExtendedUrlChangedSignal(const KUrl& url)

View file

@ -28,7 +28,7 @@
#include "views/additionalinfoaccessor.h"
AdditionalInfoDialog::AdditionalInfoDialog(QWidget* parent,
KFileItemDelegate::InformationList infoList) :
const QList<DolphinView::AdditionalInfo>& infoList) :
KDialog(parent),
m_infoList(infoList),
m_checkBoxes()
@ -49,8 +49,8 @@ AdditionalInfoDialog::AdditionalInfoDialog(QWidget* parent,
// Add checkboxes
const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance();
const KFileItemDelegate::InformationList keys = infoAccessor.keys();
foreach (const KFileItemDelegate::Information info, keys) {
const QList<DolphinView::AdditionalInfo> keys = infoAccessor.keys();
foreach (DolphinView::AdditionalInfo info, keys) {
QCheckBox* checkBox = new QCheckBox(infoAccessor.translation(info), mainWidget);
checkBox->setChecked(infoList.contains(info));
layout->addWidget(checkBox);
@ -75,7 +75,7 @@ AdditionalInfoDialog::~AdditionalInfoDialog()
saveDialogSize(dialogConfig, KConfigBase::Persistent);
}
KFileItemDelegate::InformationList AdditionalInfoDialog::informationList() const
QList<DolphinView::AdditionalInfo> AdditionalInfoDialog::informationList() const
{
return m_infoList;
}
@ -84,9 +84,9 @@ void AdditionalInfoDialog::slotOk()
{
m_infoList.clear();
const KFileItemDelegate::InformationList keys = AdditionalInfoAccessor::instance().keys();
const QList<DolphinView::AdditionalInfo> keys = AdditionalInfoAccessor::instance().keys();
int index = 0;
foreach (const KFileItemDelegate::Information info, keys) {
foreach (DolphinView::AdditionalInfo info, keys) {
if (m_checkBoxes[index]->isChecked()) {
m_infoList.append(info);
}

View file

@ -20,8 +20,8 @@
#ifndef ADDITIONALINFODIALOG_H
#define ADDITIONALINFODIALOG_H
#include <views/dolphinview.h>
#include <KDialog>
#include <KFileItemDelegate>
#include <QList>
class QCheckBox;
@ -34,15 +34,15 @@ class AdditionalInfoDialog : public KDialog
Q_OBJECT
public:
AdditionalInfoDialog(QWidget* parent, KFileItemDelegate::InformationList infoList);
AdditionalInfoDialog(QWidget* parent, const QList<DolphinView::AdditionalInfo>& infoList);
virtual ~AdditionalInfoDialog();
KFileItemDelegate::InformationList informationList() const;
QList<DolphinView::AdditionalInfo> informationList() const;
private slots:
void slotOk();
private:
KFileItemDelegate::InformationList m_infoList;
QList<DolphinView::AdditionalInfo> m_infoList;
QList<QCheckBox*> m_checkBoxes;
};

View file

@ -32,8 +32,8 @@ ApplyViewPropsJob::ApplyViewPropsJob(const KUrl& dir,
{
m_viewProps = new ViewProperties(dir);
m_viewProps->setViewMode(viewProps.viewMode());
m_viewProps->setShowPreview(viewProps.showPreview());
m_viewProps->setShowHiddenFiles(viewProps.showHiddenFiles());
m_viewProps->setPreviewsShown(viewProps.previewsShown());
m_viewProps->setHiddenFilesShown(viewProps.hiddenFilesShown());
m_viewProps->setSorting(viewProps.sorting());
m_viewProps->setSortOrder(viewProps.sortOrder());

View file

@ -1,4 +0,0 @@
File=dolphin_columnmodesettings.kcfg
ClassName=ColumnModeSettings
Singleton=false
Mutators=true

View file

@ -3,19 +3,19 @@
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<include>kiconloader.h</include>
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd">
<include>kglobalsettings.h</include>
<include>kiconloader.h</include>
<kcfgfile name="dolphinrc"/>
<group name="ColumnMode">
<entry name="FontFamily" type="String">
<label>Font family</label>
<default code="true">KGlobalSettings::generalFont().family()</default>
</entry>
<group name="CompactMode">
<entry name="UseSystemFont" type="Bool">
<label>Use system font</label>
<default>true</default>
</entry>
<entry name="FontFamily" type="String">
<label>Font family</label>
<default code="true">KGlobalSettings::generalFont().family()</default>
</entry>
<entry name="FontSize" type="Double">
<label>Font size</label>
<default code="true">KGlobalSettings::generalFont().pointSizeF()</default>
@ -30,15 +30,11 @@
</entry>
<entry name="IconSize" type="Int">
<label>Icon size</label>
<default code="true">KIconLoader::SizeSmall</default>
<default code="true">KIconLoader::SizeMedium</default>
</entry>
<entry name="PreviewSize" type="Int">
<label>Preview size</label>
<default code="true">KIconLoader::SizeLarge</default>
</entry>
<entry name="ColumnWidth" type="Int">
<label>Column width</label>
<default>250</default>
<default code="true">KIconLoader::SizeHuge</default>
</entry>
</group>
</kcfg>

View file

@ -0,0 +1,4 @@
File=dolphin_compactmodesettings.kcfg
ClassName=CompactModeSettings
Singleton=yes
Mutators=true

View file

@ -36,10 +36,6 @@
<label>Preview size</label>
<default code="true">KIconLoader::SizeLarge</default>
</entry>
<entry name="ExpandableFolders" type="Bool">
<label>Expandable folders</label>
<default>false</default>
</entry>
<entry name="ColumnPositions" type="IntList">
<label>Position of columns</label>
<default>0,1,2,3,4,5,6,7,8</default>

View file

@ -1,4 +1,4 @@
File=dolphin_detailsmodesettings.kcfg
ClassName=DetailsModeSettings
Singleton=false
Singleton=yes
Mutators=true

View file

@ -7,8 +7,8 @@
<kcfgfile arg="true" />
<group name="Settings">
<entry name="ShowHiddenFiles" key="ShowDotFiles" type="Bool" >
<label context="@label">Show hidden files</label>
<entry name="HiddenFilesShown" type="Bool" >
<label context="@label">Hidden files shown</label>
<whatsthis context="@info:whatsthis">When this option is enabled hidden files, such as those starting with a '.', will be shown in the file view.</whatsthis>
<default>false</default>
</entry>
@ -18,7 +18,7 @@
<entry name="Version" type="Int" >
<label context="@label">Version</label>
<whatsthis context="@info:whatsthis">This option defines the used version of the view properties.</whatsthis>
<default>1</default>
<default>2</default>
<min>1</min>
</entry>
@ -26,12 +26,10 @@
<label context="@label">View Mode</label>
<whatsthis context="@info:whatsthis">This option controls the style of the view. Currently supported values include icons (0), details (1) and column (2) views.</whatsthis>
<default>DolphinView::IconsView</default>
<min>0</min>
<max code="true">DolphinView::MaxModeEnum</max>
</entry>
<entry name="ShowPreview" type="Bool" >
<label context="@label">Show preview</label>
<entry name="PreviewsShown" type="Bool" >
<label context="@label">Previews shown</label>
<whatsthis context="@info:whatsthis">When this option is enabled, a preview of the file content is shown as an icon.</whatsthis>
<default>false</default>
</entry>
@ -46,8 +44,6 @@
<label context="@label">Sort files by</label>
<whatsthis context="@info:whatsthis">This option defines which attribute (name, size, date, etc.) sorting is performed on.</whatsthis>
<default code="true">DolphinView::SortByName</default>
<min>0</min>
<max code="true">DolphinView::MaxSortingEnum</max>
</entry>
<entry name="SortOrder" type="Int" >
@ -62,12 +58,7 @@
<default>true</default>
</entry>
<entry name="AdditionalInfo" type="Int">
<label context="@label">Additional information (deprecated, use AdditionInfoV2 instead)</label>
<default>0</default>
</entry>
<entry name="AdditionalInfoV2" type="StringList">
<entry name="AdditionalInfo" type="StringList">
<label context="@label">Additional information</label>
<default></default>
</entry>

View file

@ -6,13 +6,8 @@
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd">
<include>kglobalsettings.h</include>
<include>kiconloader.h</include>
<include>QListView</include>
<kcfgfile name="dolphinrc"/>
<group name="IconsMode">
<entry name="Arrangement" type="Int">
<label>Arrangement</label>
<default code="true">QListView::TopToBottom</default>
</entry>
<entry name="UseSystemFont" type="Bool">
<label>Use system font</label>
<default>true</default>
@ -33,38 +28,17 @@
<label>Font weight</label>
<default>0</default>
</entry>
<entry name="ItemHeight" type="Int">
<label>Item height</label>
<!--
check 'void IconsViewSettingsPage::applySettings()' as reference (iconsviewsettingspage.cpp):
itemHeight += fontHeight * numberOfTextlines + 10;
/-->
<default code="true">KIconLoader::SizeMedium + QFontMetrics(KGlobalSettings::generalFont()).height() * 2 + 10</default>
</entry>
<entry name="ItemWidth" type="Int">
<label>Item width</label>
<!--
check 'void IconsViewSettingsPage::applySettings()' as reference (iconsviewsettingspage.cpp):
itemWidth = TopToBottomBase + textSizeIndex * TopToBottomInc;
/-->
<default>96</default>
</entry>
<entry name="GridSpacing" type="Int">
<label>Grid spacing</label>
<default>8</default>
</entry>
<entry name="IconSize" type="Int">
<label>Icon size</label>
<default code="true">KIconLoader::SizeMedium</default>
</entry>
<entry name="NumberOfTextlines" type="Int">
<label>Number of textlines</label>
<!-- don't forget adjusting the "ItemHeight" too when changing this value /-->
<default>2</default>
</entry>
<entry name="PreviewSize" type="Int">
<label>Preview size</label>
<default code="true">KIconLoader::SizeHuge</default>
</entry>
<entry name="TextWidthIndex" type="Int">
<label>Text width index</label>
<default>1</default>
</entry>
</group>
</kcfg>

View file

@ -1,4 +1,4 @@
File=dolphin_iconsmodesettings.kcfg
ClassName=IconsModeSettings
Singleton=false
Singleton=yes
Mutators=true

View file

@ -25,7 +25,6 @@
#include <KLocale>
#include <KStandardDirs>
#include "dolphin_columnmodesettings.h"
#include "dolphin_detailsmodesettings.h"
#include "dolphin_generalsettings.h"
#include "dolphin_iconsmodesettings.h"
@ -45,17 +44,11 @@ DolphinSettings& DolphinSettings::instance()
void DolphinSettings::save()
{
m_generalSettings->writeConfig();
m_iconsModeSettings->writeConfig();
m_detailsModeSettings->writeConfig();
m_columnModeSettings->writeConfig();
}
DolphinSettings::DolphinSettings()
{
m_generalSettings = new GeneralSettings();
m_iconsModeSettings = new IconsModeSettings();
m_detailsModeSettings = new DetailsModeSettings();
m_columnModeSettings = new ColumnModeSettings();
m_placesModel = new KFilePlacesModel();
}
@ -64,15 +57,6 @@ DolphinSettings::~DolphinSettings()
delete m_generalSettings;
m_generalSettings = 0;
delete m_iconsModeSettings;
m_iconsModeSettings = 0;
delete m_detailsModeSettings;
m_detailsModeSettings = 0;
delete m_columnModeSettings;
m_columnModeSettings = 0;
delete m_placesModel;
m_placesModel = 0;
}

View file

@ -23,12 +23,11 @@
#include <libdolphin_export.h>
class ColumnModeSettings;
class DetailsModeSettings;
class GeneralSettings;
class IconsModeSettings;
class KFilePlacesModel;
// TODO: Remove this class completely and just work with the settings directly instead
/**
* @brief Manages and stores all settings from Dolphin.
*
@ -46,9 +45,6 @@ public:
static DolphinSettings& instance();
GeneralSettings* generalSettings() const;
IconsModeSettings* iconsModeSettings() const;
DetailsModeSettings* detailsModeSettings() const;
ColumnModeSettings* columnModeSettings() const;
KFilePlacesModel* placesModel() const;
virtual void save();
@ -59,9 +55,6 @@ protected:
private:
GeneralSettings* m_generalSettings;
IconsModeSettings* m_iconsModeSettings;
DetailsModeSettings* m_detailsModeSettings;
ColumnModeSettings* m_columnModeSettings;
KFilePlacesModel* m_placesModel;
};
@ -70,21 +63,6 @@ inline GeneralSettings* DolphinSettings::generalSettings() const
return m_generalSettings;
}
inline IconsModeSettings* DolphinSettings::iconsModeSettings() const
{
return m_iconsModeSettings;
}
inline DetailsModeSettings* DolphinSettings::detailsModeSettings() const
{
return m_detailsModeSettings;
}
inline ColumnModeSettings* DolphinSettings::columnModeSettings() const
{
return m_columnModeSettings;
}
inline KFilePlacesModel* DolphinSettings::placesModel() const
{
return m_placesModel;

View file

@ -25,7 +25,6 @@
#include <KPluginFactory>
#include <KPluginLoader>
#include <settings/viewmodes/columnviewsettingspage.h>
#include <settings/viewmodes/detailsviewsettingspage.h>
#include <settings/viewmodes/iconsviewsettingspage.h>
@ -59,19 +58,15 @@ DolphinViewModesConfigModule::DolphinViewModesConfigModule(QWidget* parent, cons
tabWidget->addTab(iconsPage, KIcon("view-list-icons"), i18nc("@title:tab", "Icons"));
connect(iconsPage, SIGNAL(changed()), this, SLOT(changed()));
// TODO: initialize 'Compact' tab
// initialize 'Details' tab
DetailsViewSettingsPage* detailsPage = new DetailsViewSettingsPage(tabWidget);
tabWidget->addTab(detailsPage, KIcon("view-list-details"), i18nc("@title:tab", "Details"));
tabWidget->addTab(detailsPage, KIcon("view-list-text"), i18nc("@title:tab", "Details"));
connect(detailsPage, SIGNAL(changed()), this, SLOT(changed()));
// initialize 'Column' tab
ColumnViewSettingsPage* columnPage = new ColumnViewSettingsPage(tabWidget);
tabWidget->addTab(columnPage, KIcon("view-file-columns"), i18nc("@title:tab", "Column"));
connect(columnPage, SIGNAL(changed()), this, SLOT(changed()));
m_pages.append(iconsPage);
m_pages.append(detailsPage);
m_pages.append(columnPage);
topLayout->addWidget(tabWidget, 0, 0);
}

View file

@ -1,158 +0,0 @@
/***************************************************************************
* Copyright (C) 2006 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 "columnviewsettingspage.h"
#include "dolphinfontrequester.h"
#include <dolphin_columnmodesettings.h>
#include "iconsizegroupbox.h"
#include <KDialog>
#include <KLocale>
#include <KComboBox>
#include <settings/dolphinsettings.h>
#include <QButtonGroup>
#include <QCheckBox>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QSlider>
#include <QRadioButton>
#include <views/zoomlevelinfo.h>
ColumnViewSettingsPage::ColumnViewSettingsPage(QWidget* parent) :
ViewSettingsPageBase(parent),
m_iconSizeGroupBox(0),
m_fontRequester(0),
m_textWidthBox(0)
{
const int spacing = KDialog::spacingHint();
const int margin = KDialog::marginHint();
const QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
setSpacing(spacing);
setMargin(margin);
// Create "Icon" properties
m_iconSizeGroupBox = new IconSizeGroupBox(this);
m_iconSizeGroupBox->setSizePolicy(sizePolicy);
const int min = ZoomLevelInfo::minimumLevel();
const int max = ZoomLevelInfo::maximumLevel();
m_iconSizeGroupBox->setDefaultSizeRange(min, max);
m_iconSizeGroupBox->setPreviewSizeRange(min, max);
// create "Text" properties
QGroupBox* textGroup = new QGroupBox(i18nc("@title:group", "Text"), this);
textGroup->setSizePolicy(sizePolicy);
QLabel* fontLabel = new QLabel(i18nc("@label:listbox", "Font:"), textGroup);
m_fontRequester = new DolphinFontRequester(textGroup);
QLabel* textWidthLabel = new QLabel(i18nc("@label:listbox", "Text width:"), textGroup);
m_textWidthBox = new KComboBox(textGroup);
m_textWidthBox->addItem(i18nc("@item:inlistbox Text width", "Small"));
m_textWidthBox->addItem(i18nc("@item:inlistbox Text width", "Medium"));
m_textWidthBox->addItem(i18nc("@item:inlistbox Text width", "Large"));
m_textWidthBox->addItem(i18nc("@item:inlistbox Text width", "Huge"));
QGridLayout* textGroupLayout = new QGridLayout(textGroup);
textGroupLayout->addWidget(fontLabel, 0, 0, Qt::AlignRight);
textGroupLayout->addWidget(m_fontRequester, 0, 1);
textGroupLayout->addWidget(textWidthLabel, 1, 0, Qt::AlignRight);
textGroupLayout->addWidget(m_textWidthBox, 1, 1);
// Add a dummy widget with no restriction regarding
// a vertical resizing. This assures that the dialog layout
// is not stretched vertically.
new QWidget(this);
loadSettings();
connect(m_iconSizeGroupBox, SIGNAL(defaultSizeChanged(int)), this, SIGNAL(changed()));
connect(m_iconSizeGroupBox, SIGNAL(previewSizeChanged(int)), this, SIGNAL(changed()));
connect(m_fontRequester, SIGNAL(changed()), this, SIGNAL(changed()));
connect(m_textWidthBox, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed()));
}
ColumnViewSettingsPage::~ColumnViewSettingsPage()
{
}
void ColumnViewSettingsPage::applySettings()
{
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
const int iconSize = ZoomLevelInfo::iconSizeForZoomLevel(m_iconSizeGroupBox->defaultSizeValue());
const int previewSize = ZoomLevelInfo::iconSizeForZoomLevel(m_iconSizeGroupBox->previewSizeValue());
settings->setIconSize(iconSize);
settings->setPreviewSize(previewSize);
const QFont font = m_fontRequester->font();
settings->setUseSystemFont(m_fontRequester->mode() == DolphinFontRequester::SystemFont);
settings->setFontFamily(font.family());
settings->setFontSize(font.pointSizeF());
settings->setItalicFont(font.italic());
settings->setFontWeight(font.weight());
const int columnWidth = BaseTextWidth + (m_textWidthBox->currentIndex() * TextInc);
settings->setColumnWidth(columnWidth);
settings->writeConfig();
}
void ColumnViewSettingsPage::restoreDefaults()
{
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
settings->useDefaults(true);
loadSettings();
settings->useDefaults(false);
}
void ColumnViewSettingsPage::loadSettings()
{
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
const QSize iconSize(settings->iconSize(), settings->iconSize());
const int iconSizeValue = ZoomLevelInfo::zoomLevelForIconSize(iconSize);
m_iconSizeGroupBox->setDefaultSizeValue(iconSizeValue);
const QSize previewSize(settings->previewSize(), settings->previewSize());
const int previewSizeValue = ZoomLevelInfo::zoomLevelForIconSize(previewSize);
m_iconSizeGroupBox->setPreviewSizeValue(previewSizeValue);
if (settings->useSystemFont()) {
m_fontRequester->setMode(DolphinFontRequester::SystemFont);
} else {
QFont font(settings->fontFamily(),
qRound(settings->fontSize()));
font.setItalic(settings->italicFont());
font.setWeight(settings->fontWeight());
font.setPointSizeF(settings->fontSize());
m_fontRequester->setMode(DolphinFontRequester::CustomFont);
m_fontRequester->setCustomFont(font);
}
m_textWidthBox->setCurrentIndex((settings->columnWidth() - BaseTextWidth) / TextInc);
}
#include "columnviewsettingspage.moc"

View file

@ -26,8 +26,6 @@
#include <KDialog>
#include <KLocale>
#include <settings/dolphinsettings.h>
#include <QButtonGroup>
#include <QCheckBox>
#include <QComboBox>
@ -42,8 +40,7 @@
DetailsViewSettingsPage::DetailsViewSettingsPage(QWidget* parent) :
ViewSettingsPageBase(parent),
m_iconSizeGroupBox(0),
m_fontRequester(0),
m_expandableFolders(0)
m_fontRequester(0)
{
const int spacing = KDialog::spacingHint();
const int margin = KDialog::marginHint();
@ -72,9 +69,6 @@ DetailsViewSettingsPage::DetailsViewSettingsPage(QWidget* parent) :
textLayout->addWidget(fontLabel, 0, Qt::AlignRight);
textLayout->addWidget(m_fontRequester);
// create "Expandable Folders" checkbox
m_expandableFolders = new QCheckBox(i18nc("@option:check", "Expandable folders"), this);
// Add a dummy widget with no restriction regarding
// a vertical resizing. This assures that the dialog layout
// is not stretched vertically.
@ -85,7 +79,6 @@ DetailsViewSettingsPage::DetailsViewSettingsPage(QWidget* parent) :
connect(m_iconSizeGroupBox, SIGNAL(defaultSizeChanged(int)), this, SIGNAL(changed()));
connect(m_iconSizeGroupBox, SIGNAL(previewSizeChanged(int)), this, SIGNAL(changed()));
connect(m_fontRequester, SIGNAL(changed()), this, SIGNAL(changed()));
connect(m_expandableFolders, SIGNAL(toggled(bool)), this, SIGNAL(changed()));
}
DetailsViewSettingsPage::~DetailsViewSettingsPage()
@ -94,58 +87,49 @@ DetailsViewSettingsPage::~DetailsViewSettingsPage()
void DetailsViewSettingsPage::applySettings()
{
DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
const int iconSize = ZoomLevelInfo::iconSizeForZoomLevel(m_iconSizeGroupBox->defaultSizeValue());
const int previewSize = ZoomLevelInfo::iconSizeForZoomLevel(m_iconSizeGroupBox->previewSizeValue());
settings->setIconSize(iconSize);
settings->setPreviewSize(previewSize);
DetailsModeSettings::setIconSize(iconSize);
DetailsModeSettings::setPreviewSize(previewSize);
const QFont font = m_fontRequester->font();
settings->setUseSystemFont(m_fontRequester->mode() == DolphinFontRequester::SystemFont);
settings->setFontFamily(font.family());
settings->setFontSize(font.pointSizeF());
settings->setItalicFont(font.italic());
settings->setFontWeight(font.weight());
DetailsModeSettings::setUseSystemFont(m_fontRequester->mode() == DolphinFontRequester::SystemFont);
DetailsModeSettings::setFontFamily(font.family());
DetailsModeSettings::setFontSize(font.pointSizeF());
DetailsModeSettings::setItalicFont(font.italic());
DetailsModeSettings::setFontWeight(font.weight());
settings->setExpandableFolders(m_expandableFolders->isChecked());
settings->writeConfig();
DetailsModeSettings::self()->writeConfig();
}
void DetailsViewSettingsPage::restoreDefaults()
{
DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
settings->useDefaults(true);
DetailsModeSettings::self()->useDefaults(true);
loadSettings();
settings->useDefaults(false);
DetailsModeSettings::self()->useDefaults(false);
}
void DetailsViewSettingsPage::loadSettings()
{
DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
const QSize iconSize(settings->iconSize(), settings->iconSize());
const QSize iconSize(DetailsModeSettings::iconSize(), DetailsModeSettings::iconSize());
const int iconSizeValue = ZoomLevelInfo::zoomLevelForIconSize(iconSize);
m_iconSizeGroupBox->setDefaultSizeValue(iconSizeValue);
const QSize previewSize(settings->previewSize(), settings->previewSize());
const QSize previewSize(DetailsModeSettings::previewSize(), DetailsModeSettings::previewSize());
const int previewSizeValue = ZoomLevelInfo::zoomLevelForIconSize(previewSize);
m_iconSizeGroupBox->setPreviewSizeValue(previewSizeValue);
if (settings->useSystemFont()) {
if (DetailsModeSettings::useSystemFont()) {
m_fontRequester->setMode(DolphinFontRequester::SystemFont);
} else {
QFont font(settings->fontFamily(),
qRound(settings->fontSize()));
font.setItalic(settings->italicFont());
font.setWeight(settings->fontWeight());
font.setPointSizeF(settings->fontSize());
QFont font(DetailsModeSettings::fontFamily(),
qRound(DetailsModeSettings::fontSize()));
font.setItalic(DetailsModeSettings::italicFont());
font.setWeight(DetailsModeSettings::fontWeight());
font.setPointSizeF(DetailsModeSettings::fontSize());
m_fontRequester->setMode(DolphinFontRequester::CustomFont);
m_fontRequester->setCustomFont(font);
}
m_expandableFolders->setChecked(settings->expandableFolders());
}
#include "detailsviewsettingspage.moc"

View file

@ -24,7 +24,6 @@
class DolphinFontRequester;
class IconSizeGroupBox;
class QCheckBox;
/**
* @brief Represents the page from the Dolphin Settings which allows
@ -54,7 +53,6 @@ private:
private:
IconSizeGroupBox* m_iconSizeGroupBox;
DolphinFontRequester* m_fontRequester;
QCheckBox* m_expandableFolders;
};
#endif

View file

@ -20,7 +20,6 @@
#include "iconsviewsettingspage.h"
#include "dolphinfontrequester.h"
#include "settings/dolphinsettings.h"
#include "iconsizegroupbox.h"
#include "dolphin_iconsmodesettings.h"
@ -35,10 +34,8 @@
#include <QCheckBox>
#include <QGroupBox>
#include <QLabel>
#include <QListView>
#include <QPushButton>
#include <QGridLayout>
#include <QVBoxLayout>
#include <views/zoomlevelinfo.h>
@ -46,10 +43,7 @@ IconsViewSettingsPage::IconsViewSettingsPage(QWidget* parent) :
ViewSettingsPageBase(parent),
m_iconSizeGroupBox(0),
m_textWidthBox(0),
m_fontRequester(0),
m_textlinesCountBox(0),
m_arrangementBox(0),
m_gridSpacingBox(0)
m_fontRequester(0)
{
const int spacing = KDialog::spacingHint();
const int margin = KDialog::marginHint();
@ -67,7 +61,7 @@ IconsViewSettingsPage::IconsViewSettingsPage(QWidget* parent) :
m_iconSizeGroupBox->setDefaultSizeRange(min, max);
m_iconSizeGroupBox->setPreviewSizeRange(min, max);
// create 'Text' group for selecting the font, the number of lines
// Create 'Text' group for selecting the font, the number of lines
// and the text width
QGroupBox* textGroup = new QGroupBox(i18nc("@title:group", "Text"), this);
textGroup->setSizePolicy(sizePolicy);
@ -75,11 +69,6 @@ IconsViewSettingsPage::IconsViewSettingsPage(QWidget* parent) :
QLabel* fontLabel = new QLabel(i18nc("@label:listbox", "Font:"), textGroup);
m_fontRequester = new DolphinFontRequester(textGroup);
QLabel* textlinesCountLabel = new QLabel(i18nc("@label:textbox", "Number of lines:"), textGroup);
m_textlinesCountBox = new KIntSpinBox(textGroup);
m_textlinesCountBox->setMinimum(1);
m_textlinesCountBox->setMaximum(5);
QLabel* textWidthLabel = new QLabel(i18nc("@label:listbox", "Text width:"), textGroup);
m_textWidthBox = new KComboBox(textGroup);
m_textWidthBox->addItem(i18nc("@item:inlistbox Text width", "Small"));
@ -90,33 +79,9 @@ IconsViewSettingsPage::IconsViewSettingsPage(QWidget* parent) :
QGridLayout* textGroupLayout = new QGridLayout(textGroup);
textGroupLayout->addWidget(fontLabel, 0, 0, Qt::AlignRight);
textGroupLayout->addWidget(m_fontRequester, 0, 1);
textGroupLayout->addWidget(textlinesCountLabel, 1, 0, Qt::AlignRight);
textGroupLayout->addWidget(m_textlinesCountBox, 1, 1);
textGroupLayout->addWidget(textWidthLabel, 2, 0, Qt::AlignRight);
textGroupLayout->addWidget(m_textWidthBox, 2, 1);
// create the 'Grid' group for selecting the arrangement and the grid spacing
QGroupBox* gridGroup = new QGroupBox(i18nc("@title:group", "Grid"), this);
gridGroup->setSizePolicy(sizePolicy);
QLabel* arrangementLabel = new QLabel(i18nc("@label:listbox", "Arrangement:"), gridGroup);
m_arrangementBox = new KComboBox(gridGroup);
m_arrangementBox->addItem(i18nc("@item:inlistbox Arrangement", "Columns"));
m_arrangementBox->addItem(i18nc("@item:inlistbox Arrangement", "Rows"));
QLabel* gridSpacingLabel = new QLabel(i18nc("@label:listbox", "Grid spacing:"), gridGroup);
m_gridSpacingBox = new KComboBox(gridGroup);
m_gridSpacingBox->addItem(i18nc("@item:inlistbox Grid spacing", "None"));
m_gridSpacingBox->addItem(i18nc("@item:inlistbox Grid spacing", "Small"));
m_gridSpacingBox->addItem(i18nc("@item:inlistbox Grid spacing", "Medium"));
m_gridSpacingBox->addItem(i18nc("@item:inlistbox Grid spacing", "Large"));
QGridLayout* gridGroupLayout = new QGridLayout(gridGroup);
gridGroupLayout->addWidget(arrangementLabel, 0, 0, Qt::AlignRight);
gridGroupLayout->addWidget(m_arrangementBox, 0, 1);
gridGroupLayout->addWidget(gridSpacingLabel, 1, 0, Qt::AlignRight);
gridGroupLayout->addWidget(m_gridSpacingBox, 1, 1);
// Add a dummy widget with no restriction regarding
// a vertical resizing. This assures that the dialog layout
// is not stretched vertically.
@ -127,10 +92,7 @@ IconsViewSettingsPage::IconsViewSettingsPage(QWidget* parent) :
connect(m_iconSizeGroupBox, SIGNAL(defaultSizeChanged(int)), this, SIGNAL(changed()));
connect(m_iconSizeGroupBox, SIGNAL(previewSizeChanged(int)), this, SIGNAL(changed()));
connect(m_fontRequester, SIGNAL(changed()), this, SIGNAL(changed()));
connect(m_textlinesCountBox, SIGNAL(valueChanged(int)), this, SIGNAL(changed()));
connect(m_textWidthBox, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed()));
connect(m_arrangementBox, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed()));
connect(m_gridSpacingBox, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed()));
}
IconsViewSettingsPage::~IconsViewSettingsPage()
@ -139,108 +101,53 @@ IconsViewSettingsPage::~IconsViewSettingsPage()
void IconsViewSettingsPage::applySettings()
{
IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings();
const int iconSize = ZoomLevelInfo::iconSizeForZoomLevel(m_iconSizeGroupBox->defaultSizeValue());
const int previewSize = ZoomLevelInfo::iconSizeForZoomLevel(m_iconSizeGroupBox->previewSizeValue());
settings->setIconSize(iconSize);
settings->setPreviewSize(previewSize);
IconsModeSettings::setIconSize(iconSize);
IconsModeSettings::setPreviewSize(previewSize);
const QFont font = m_fontRequester->font();
const int fontHeight = QFontMetrics(font).height();
IconsModeSettings::setUseSystemFont(m_fontRequester->mode() == DolphinFontRequester::SystemFont);
IconsModeSettings::setFontFamily(font.family());
IconsModeSettings::setFontSize(font.pointSizeF());
IconsModeSettings::setItalicFont(font.italic());
IconsModeSettings::setFontWeight(font.weight());
const int arrangement = (m_arrangementBox->currentIndex() == 0) ?
QListView::LeftToRight :
QListView::TopToBottom;
settings->setArrangement(arrangement);
IconsModeSettings::setTextWidthIndex(m_textWidthBox->currentIndex());
const int numberOfTextlines = m_textlinesCountBox->value();
const int defaultSize = settings->iconSize();
int itemWidth = defaultSize;
int itemHeight = defaultSize;
const int textSizeIndex = m_textWidthBox->currentIndex();
if (arrangement == QListView::TopToBottom) {
itemWidth += TopToBottomBase + textSizeIndex * TopToBottomInc;
itemHeight += fontHeight * numberOfTextlines + 10;
} else {
itemWidth += LeftToRightBase + textSizeIndex * LeftToRightInc;
}
settings->setItemWidth(itemWidth);
settings->setItemHeight(itemHeight);
settings->setUseSystemFont(m_fontRequester->mode() == DolphinFontRequester::SystemFont);
settings->setFontFamily(font.family());
settings->setFontSize(font.pointSizeF());
settings->setItalicFont(font.italic());
settings->setFontWeight(font.weight());
settings->setNumberOfTextlines(numberOfTextlines);
const int index = m_gridSpacingBox->currentIndex();
if (index == 0) {
// No grid spacing
settings->setGridSpacing(0);
} else {
settings->setGridSpacing(GridSpacingBase + (index - 1) * GridSpacingInc);
}
settings->writeConfig();
IconsModeSettings::self()->writeConfig();
}
void IconsViewSettingsPage::restoreDefaults()
{
IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings();
settings->useDefaults(true);
IconsModeSettings::self()->useDefaults(true);
loadSettings();
settings->useDefaults(false);
IconsModeSettings::self()->useDefaults(false);
}
void IconsViewSettingsPage::loadSettings()
{
IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings();
const QSize iconSize(settings->iconSize(), settings->iconSize());
const QSize iconSize(IconsModeSettings::iconSize(), IconsModeSettings::iconSize());
const int iconSizeValue = ZoomLevelInfo::zoomLevelForIconSize(iconSize);
m_iconSizeGroupBox->setDefaultSizeValue(iconSizeValue);
const QSize previewSize(settings->previewSize(), settings->previewSize());
const QSize previewSize(IconsModeSettings::previewSize(), IconsModeSettings::previewSize());
const int previewSizeValue = ZoomLevelInfo::zoomLevelForIconSize(previewSize);
m_iconSizeGroupBox->setPreviewSizeValue(previewSizeValue);
if (settings->useSystemFont()) {
if (IconsModeSettings::useSystemFont()) {
m_fontRequester->setMode(DolphinFontRequester::SystemFont);
} else {
QFont font(settings->fontFamily(),
qRound(settings->fontSize()));
font.setItalic(settings->italicFont());
font.setWeight(settings->fontWeight());
font.setPointSizeF(settings->fontSize());
QFont font(IconsModeSettings::fontFamily(),
qRound(IconsModeSettings::fontSize()));
font.setItalic(IconsModeSettings::italicFont());
font.setWeight(IconsModeSettings::fontWeight());
font.setPointSizeF(IconsModeSettings::fontSize());
m_fontRequester->setMode(DolphinFontRequester::CustomFont);
m_fontRequester->setCustomFont(font);
}
m_textlinesCountBox->setValue(settings->numberOfTextlines());
const bool leftToRightArrangement = (settings->arrangement() == QListView::LeftToRight);
int textWidthIndex = 0;
const int remainingWidth = settings->itemWidth() - settings->iconSize();
if (leftToRightArrangement) {
textWidthIndex = (remainingWidth - LeftToRightBase) / LeftToRightInc;
} else {
textWidthIndex = (remainingWidth - TopToBottomBase) / TopToBottomInc;
}
// ensure that chosen index is always valid
textWidthIndex = qMax(textWidthIndex, 0);
textWidthIndex = qMin(textWidthIndex, m_textWidthBox->count() - 1);
m_textWidthBox->setCurrentIndex(textWidthIndex);
m_arrangementBox->setCurrentIndex(leftToRightArrangement ? 0 : 1);
const int spacing = settings->gridSpacing();
const int index = (spacing <= 0) ? 0 : 1 + (spacing - GridSpacingBase) / GridSpacingInc;
m_gridSpacingBox->setCurrentIndex(index);
m_textWidthBox->setCurrentIndex(IconsModeSettings::textWidthIndex());
}
#include "iconsviewsettingspage.moc"

View file

@ -20,7 +20,6 @@
#ifndef ICONSVIEWSETTINGSPAGE_H
#define ICONSVIEWSETTINGSPAGE_H
#include <views/dolphiniconsview.h>
#include "viewsettingspagebase.h"
class DolphinFontRequester;
@ -36,10 +35,7 @@ class KIntSpinBox;
* - icon size
* - preview size
* - text width
* - grid spacing
* - font
* - number of text lines
* - arrangement
*
* @see DolphinIconsViewSettings
*/
@ -65,23 +61,9 @@ private:
void loadSettings();
private:
enum
{
GridSpacingBase = 8,
GridSpacingInc = 12,
LeftToRightBase = 128,
LeftToRightInc = 64,
TopToBottomBase = 32,
TopToBottomInc = 32
};
IconSizeGroupBox* m_iconSizeGroupBox;
KComboBox* m_textWidthBox;
DolphinFontRequester* m_fontRequester;
KIntSpinBox* m_textlinesCountBox;
KComboBox* m_arrangementBox;
KComboBox* m_gridSpacingBox;
};
#endif

View file

@ -20,7 +20,6 @@
#include "viewsettingspage.h"
#include "columnviewsettingspage.h"
#include "iconsviewsettingspage.h"
#include "detailsviewsettingspage.h"
@ -46,19 +45,15 @@ ViewSettingsPage::ViewSettingsPage(QWidget* parent) :
tabWidget->addTab(iconsPage, KIcon("view-list-icons"), i18nc("@title:tab", "Icons"));
connect(iconsPage, SIGNAL(changed()), this, SIGNAL(changed()));
// TODO: initialize 'Compact' tab
// initialize 'Details' tab
DetailsViewSettingsPage* detailsPage = new DetailsViewSettingsPage(tabWidget);
tabWidget->addTab(detailsPage, KIcon("view-list-details"), i18nc("@title:tab", "Details"));
tabWidget->addTab(detailsPage, KIcon("view-list-text"), i18nc("@title:tab", "Details"));
connect(detailsPage, SIGNAL(changed()), this, SIGNAL(changed()));
// initialize 'Column' tab
ColumnViewSettingsPage* columnPage = new ColumnViewSettingsPage(tabWidget);
tabWidget->addTab(columnPage, KIcon("view-file-columns"), i18nc("@title:tab", "Column"));
connect(columnPage, SIGNAL(changed()), this, SIGNAL(changed()));
m_pages.append(iconsPage);
m_pages.append(detailsPage);
m_pages.append(columnPage);
topLayout->addWidget(tabWidget, 0, 0);
}

View file

@ -52,7 +52,6 @@
#include <QRadioButton>
#include <QBoxLayout>
#include <views/dolphinsortfilterproxymodel.h>
#include <views/viewproperties.h>
ViewPropertiesDialog::ViewPropertiesDialog(DolphinView* dolphinView) :
@ -64,7 +63,7 @@ ViewPropertiesDialog::ViewPropertiesDialog(DolphinView* dolphinView) :
m_sortOrder(0),
m_sorting(0),
m_sortFoldersFirst(0),
m_showPreview(0),
m_previewsShown(0),
m_showInGroups(0),
m_showHiddenFiles(0),
m_additionalInfo(0),
@ -97,8 +96,8 @@ ViewPropertiesDialog::ViewPropertiesDialog(DolphinView* dolphinView) :
QLabel* viewModeLabel = new QLabel(i18nc("@label:listbox", "View mode:"), propsGrid);
m_viewMode = new KComboBox(propsGrid);
m_viewMode->addItem(KIcon("view-list-icons"), i18nc("@item:inlistbox", "Icons"));
m_viewMode->addItem(KIcon("view-list-details"), i18nc("@item:inlistbox", "Details"));
m_viewMode->addItem(KIcon("view-file-columns"), i18nc("@item:inlistbox", "Column"));
m_viewMode->addItem(KIcon("feffi"), i18nc("@item:inlistbox", "Compact")); // TODO: adjust icons
m_viewMode->addItem(KIcon("view-list-text"), i18nc("@item:inlistbox", "Details"));
QLabel* sortingLabel = new QLabel(i18nc("@label:listbox", "Sorting:"), propsGrid);
QWidget* sortingBox = new QWidget(propsGrid);
@ -125,7 +124,7 @@ ViewPropertiesDialog::ViewPropertiesDialog(DolphinView* dolphinView) :
// }
#endif
m_sortFoldersFirst = new QCheckBox(i18nc("@option:check", "Show folders first"));
m_showPreview = new QCheckBox(i18nc("@option:check", "Show preview"));
m_previewsShown = new QCheckBox(i18nc("@option:check", "Show preview"));
m_showInGroups = new QCheckBox(i18nc("@option:check", "Show in groups"));
m_showHiddenFiles = new QCheckBox(i18nc("@option:check", "Show hidden files"));
@ -146,7 +145,7 @@ ViewPropertiesDialog::ViewPropertiesDialog(DolphinView* dolphinView) :
QVBoxLayout* propsBoxLayout = new QVBoxLayout(propsBox);
propsBoxLayout->addWidget(propsGrid);
propsBoxLayout->addWidget(m_sortFoldersFirst);
propsBoxLayout->addWidget(m_showPreview);
propsBoxLayout->addWidget(m_previewsShown);
propsBoxLayout->addWidget(m_showInGroups);
propsBoxLayout->addWidget(m_showHiddenFiles);
propsBoxLayout->addWidget(m_additionalInfo);
@ -163,7 +162,7 @@ ViewPropertiesDialog::ViewPropertiesDialog(DolphinView* dolphinView) :
this, SLOT(configureAdditionalInfo()));
connect(m_sortFoldersFirst, SIGNAL(clicked()),
this, SLOT(slotSortFoldersFirstChanged()));
connect(m_showPreview, SIGNAL(clicked()),
connect(m_previewsShown, SIGNAL(clicked()),
this, SLOT(slotShowPreviewChanged()));
connect(m_showInGroups, SIGNAL(clicked()),
this, SLOT(slotCategorizedSortingChanged()));
@ -249,17 +248,15 @@ void ViewPropertiesDialog::slotViewModeChanged(int index)
{
m_viewProps->setViewMode(static_cast<DolphinView::Mode>(index));
markAsDirty(true);
const DolphinView::Mode mode = m_viewProps->viewMode();
m_showInGroups->setEnabled(mode == DolphinView::IconsView);
m_additionalInfo->setEnabled(mode != DolphinView::ColumnView);
}
void ViewPropertiesDialog::slotSortingChanged(int index)
{
const DolphinView::Sorting sorting = DolphinSortFilterProxyModel::sortingForColumn(index);
m_viewProps->setSorting(sorting);
markAsDirty(true);
Q_UNUSED(index);
Q_ASSERT(false);
//const DolphinView::Sorting sorting = DolphinSortFilterProxyModel::sortingForColumn(index);
//m_viewProps->setSorting(sorting);
//markAsDirty(true);
}
void ViewPropertiesDialog::slotSortOrderChanged(int index)
@ -284,15 +281,15 @@ void ViewPropertiesDialog::slotSortFoldersFirstChanged()
void ViewPropertiesDialog::slotShowPreviewChanged()
{
const bool show = m_showPreview->isChecked();
m_viewProps->setShowPreview(show);
const bool show = m_previewsShown->isChecked();
m_viewProps->setPreviewsShown(show);
markAsDirty(true);
}
void ViewPropertiesDialog::slotShowHiddenFilesChanged()
{
const bool show = m_showHiddenFiles->isChecked();
m_viewProps->setShowHiddenFiles(show);
m_viewProps->setHiddenFilesShown(show);
markAsDirty(true);
}
@ -304,22 +301,22 @@ void ViewPropertiesDialog::markAsDirty(bool isDirty)
void ViewPropertiesDialog::configureAdditionalInfo()
{
KFileItemDelegate::InformationList info = m_viewProps->additionalInfo();
QList<DolphinView::AdditionalInfo> infoList = m_viewProps->additionalInfoList();
const bool useDefaultInfo = (m_viewProps->viewMode() == DolphinView::DetailsView) &&
(info.isEmpty() || info.contains(KFileItemDelegate::NoInformation));
(infoList.isEmpty() || infoList.contains(DolphinView::NoInfo));
if (useDefaultInfo) {
// Using the details view without any additional information (-> additional column)
// makes no sense and leads to a usability problem as no viewport area is available
// anymore. Hence as fallback provide at least a size and date column.
info.clear();
info.append(KFileItemDelegate::Size);
info.append(KFileItemDelegate::ModificationTime);
m_viewProps->setAdditionalInfo(info);
infoList.clear();
infoList.append(DolphinView::SizeInfo);
infoList.append(DolphinView::DateInfo);
m_viewProps->setAdditionalInfoList(infoList);
}
QPointer<AdditionalInfoDialog> dialog = new AdditionalInfoDialog(this, info);
QPointer<AdditionalInfoDialog> dialog = new AdditionalInfoDialog(this, infoList);
if (dialog->exec() == QDialog::Accepted) {
m_viewProps->setAdditionalInfo(dialog->informationList());
m_viewProps->setAdditionalInfoList(dialog->informationList());
markAsDirty(true);
}
delete dialog;
@ -383,9 +380,9 @@ void ViewPropertiesDialog::applyViewProperties()
m_dolphinView->setSortOrder(m_viewProps->sortOrder());
m_dolphinView->setSortFoldersFirst(m_viewProps->sortFoldersFirst());
m_dolphinView->setCategorizedSorting(m_viewProps->categorizedSorting());
m_dolphinView->setAdditionalInfo(m_viewProps->additionalInfo());
m_dolphinView->setShowPreview(m_viewProps->showPreview());
m_dolphinView->setShowHiddenFiles(m_viewProps->showHiddenFiles());
m_dolphinView->setAdditionalInfoList(m_viewProps->additionalInfoList());
m_dolphinView->setPreviewsShown(m_viewProps->previewsShown());
m_dolphinView->setHiddenFilesShown(m_viewProps->hiddenFilesShown());
m_viewProps->save();
@ -409,12 +406,12 @@ void ViewPropertiesDialog::loadSettings()
m_sortFoldersFirst->setChecked(m_viewProps->sortFoldersFirst());
// load show preview, show in groups and show hidden files settings
m_showPreview->setChecked(m_viewProps->showPreview());
m_previewsShown->setChecked(m_viewProps->previewsShown());
m_showInGroups->setChecked(m_viewProps->categorizedSorting());
m_showInGroups->setEnabled(index == DolphinView::IconsView); // only the icons view supports categorized sorting
m_showHiddenFiles->setChecked(m_viewProps->showHiddenFiles());
m_showHiddenFiles->setChecked(m_viewProps->hiddenFilesShown());
markAsDirty(false);
}

View file

@ -73,7 +73,7 @@ private:
KComboBox* m_sortOrder;
KComboBox* m_sorting;
QCheckBox* m_sortFoldersFirst;
QCheckBox* m_showPreview;
QCheckBox* m_previewsShown;
QCheckBox* m_showInGroups;
QCheckBox* m_showHiddenFiles;
QPushButton* m_additionalInfo;

View file

@ -48,10 +48,7 @@ DolphinStatusBar::DolphinStatusBar(QWidget* parent, DolphinView* view) :
m_view(view),
m_messageLabel(0),
m_spaceInfo(0),
m_zoomWidget(0),
m_zoomOut(0),
m_zoomSlider(0),
m_zoomIn(0),
m_progressBar(0),
m_stopButton(0),
m_progress(100),
@ -64,14 +61,8 @@ DolphinStatusBar::DolphinStatusBar(QWidget* parent, DolphinView* view) :
// Initialize message label
m_messageLabel = new KonqStatusBarMessageLabel(this);
// Initialize zoom slider
m_zoomWidget = new QWidget(this);
m_zoomOut = new QToolButton(m_zoomWidget);
m_zoomOut->setIcon(KIcon("file-zoom-out"));
m_zoomOut->setAutoRaise(true);
m_zoomSlider = new QSlider(Qt::Horizontal, m_zoomWidget);
// Initialize zoom widget
m_zoomSlider = new QSlider(Qt::Horizontal, this);
m_zoomSlider->setPageStep(1);
const int min = ZoomLevelInfo::minimumLevel();
@ -80,23 +71,9 @@ DolphinStatusBar::DolphinStatusBar(QWidget* parent, DolphinView* view) :
m_zoomSlider->setValue(view->zoomLevel());
updateZoomSliderToolTip(view->zoomLevel());
m_zoomIn = new QToolButton(m_zoomWidget);
m_zoomIn->setIcon(KIcon("file-zoom-in"));
m_zoomIn->setAutoRaise(true);
// Initialize zoom widget layout
QHBoxLayout* zoomWidgetLayout = new QHBoxLayout(m_zoomWidget);
zoomWidgetLayout->setSpacing(0);
zoomWidgetLayout->setMargin(0);
zoomWidgetLayout->addWidget(m_zoomOut);
zoomWidgetLayout->addWidget(m_zoomSlider);
zoomWidgetLayout->addWidget(m_zoomIn);
connect(m_zoomSlider, SIGNAL(valueChanged(int)), this, SLOT(setZoomLevel(int)));
connect(m_zoomSlider, SIGNAL(sliderMoved(int)), this, SLOT(showZoomSliderToolTip(int)));
connect(m_view, SIGNAL(zoomLevelChanged(int)), m_zoomSlider, SLOT(setValue(int)));
connect(m_zoomOut, SIGNAL(clicked()), this, SLOT(zoomOut()));
connect(m_zoomIn, SIGNAL(clicked()), this, SLOT(zoomIn()));
connect(m_view, SIGNAL(zoomLevelChanged(int, int)), this, SLOT(slotZoomLevelChanged(int, int)));
// Initialize space information
m_spaceInfo = new StatusBarSpaceInfo(this);
@ -123,8 +100,8 @@ DolphinStatusBar::DolphinStatusBar(QWidget* parent, DolphinView* view) :
// Initialize top layout and size policies
const int fontHeight = QFontMetrics(m_messageLabel->font()).height();
const int zoomWidgetHeight = m_zoomWidget->minimumSizeHint().height();
const int contentHeight = qMax(fontHeight, zoomWidgetHeight);
const int zoomSliderHeight = m_zoomSlider->minimumSizeHint().height();
const int contentHeight = qMax(fontHeight, zoomSliderHeight);
m_messageLabel->setMinimumTextHeight(contentHeight);
@ -132,14 +109,14 @@ DolphinStatusBar::DolphinStatusBar(QWidget* parent, DolphinView* view) :
m_spaceInfo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_progressBar->setMaximumSize(200, contentHeight);
m_zoomWidget->setMaximumSize(150, contentHeight);
m_zoomSlider->setMaximumSize(150, contentHeight);
m_zoomSlider->setMinimumWidth(30);
QHBoxLayout* topLayout = new QHBoxLayout(this);
topLayout->setMargin(0);
topLayout->setSpacing(4);
topLayout->addWidget(m_messageLabel);
topLayout->addWidget(m_zoomWidget);
topLayout->addWidget(m_zoomSlider);
topLayout->addWidget(m_spaceInfo);
topLayout->addWidget(m_stopButton);
topLayout->addWidget(m_progressText);
@ -317,7 +294,7 @@ void DolphinStatusBar::contextMenuEvent(QContextMenuEvent* event)
} else if (action == showZoomSliderAction) {
const bool visible = showZoomSliderAction->isChecked();
settings->setShowZoomSlider(visible);
m_zoomWidget->setVisible(visible);
m_zoomSlider->setVisible(visible);
} else if (action == showSpaceInfoAction) {
const bool visible = showSpaceInfoAction->isChecked();
settings->setShowSpaceInfo(visible);
@ -332,24 +309,10 @@ void DolphinStatusBar::updateSpaceInfoContent(const KUrl& url)
void DolphinStatusBar::setZoomLevel(int zoomLevel)
{
m_zoomOut->setEnabled(zoomLevel > m_zoomSlider->minimum());
m_zoomIn->setEnabled(zoomLevel < m_zoomSlider->maximum());
m_view->setZoomLevel(zoomLevel);
updateZoomSliderToolTip(zoomLevel);
}
void DolphinStatusBar::zoomOut()
{
const int value = m_zoomSlider->value();
m_zoomSlider->setValue(value - 1);
}
void DolphinStatusBar::zoomIn()
{
const int value = m_zoomSlider->value();
m_zoomSlider->setValue(value + 1);
}
void DolphinStatusBar::showZoomSliderToolTip(int zoomLevel)
{
updateZoomSliderToolTip(zoomLevel);
@ -360,6 +323,12 @@ void DolphinStatusBar::showZoomSliderToolTip(int zoomLevel)
QApplication::sendEvent(m_zoomSlider, &toolTipEvent);
}
void DolphinStatusBar::slotZoomLevelChanged(int current, int previous)
{
Q_UNUSED(previous);
m_zoomSlider->setValue(current);
}
void DolphinStatusBar::updateProgressInfo()
{
const bool isErrorShown = (m_messageLabel->type() == KonqStatusBarMessageLabel::Error);
@ -383,14 +352,14 @@ void DolphinStatusBar::updateProgressInfo()
void DolphinStatusBar::setExtensionsVisible(bool visible)
{
bool showSpaceInfo = visible;
bool showZoomWidget = visible;
bool showZoomSlider = visible;
if (visible) {
const GeneralSettings* settings = DolphinSettings::instance().generalSettings();
showSpaceInfo = settings->showSpaceInfo();
showZoomWidget = settings->showZoomSlider();
showZoomSlider = settings->showZoomSlider();
}
m_spaceInfo->setVisible(showSpaceInfo);
m_zoomWidget->setVisible(showZoomWidget);
m_zoomSlider->setVisible(showZoomSlider);
}
void DolphinStatusBar::updateZoomSliderToolTip(int zoomLevel)

View file

@ -137,9 +137,8 @@ private slots:
*/
void setZoomLevel(int zoomLevel);
void zoomOut();
void zoomIn();
void showZoomSliderToolTip(int zoomLevel);
void slotZoomLevelChanged(int current, int previous);
void updateProgressInfo();
@ -163,10 +162,7 @@ private:
KonqStatusBarMessageLabel* m_messageLabel;
StatusBarSpaceInfo* m_spaceInfo;
QWidget* m_zoomWidget;
QToolButton* m_zoomOut;
QSlider* m_zoomSlider;
QToolButton* m_zoomIn;
QLabel* m_progressText;
QProgressBar* m_progressBar;

View file

@ -1,9 +1,27 @@
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BUILD_DIR}/.. ${KDE4_INCLUDES} )
# DolphinDetailsView
kde4_add_unit_test(dolphindetailsviewtest TEST dolphindetailsviewtest.cpp testdir.cpp testbase.cpp ../views/zoomlevelinfo.cpp)
target_link_libraries(dolphindetailsviewtest dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
# KFileItemListViewTest
set(kfileitemlistviewtest_SRCS
kfileitemlistviewtest.cpp
testdir.cpp
../kitemviews/kfileitemmodel.cpp
../kitemviews/kfileitemlistview.cpp
../kitemviews/kitemmodelbase.cpp
../kitemviews/kitemlistview.cpp
)
kde4_add_unit_test(kfileitemlistviewtest TEST ${kfileitemlistviewtest_SRCS})
target_link_libraries(kfileitemlistviewtest dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
# KFileItemModelTest
set(kfileitemmodeltest_SRCS
kfileitemmodeltest.cpp
testdir.cpp
../kitemviews/kfileitemmodel.cpp
../kitemviews/kitemmodelbase.cpp
)
kde4_add_unit_test(kfileitemmodeltest TEST ${kfileitemmodeltest_SRCS})
target_link_libraries(kfileitemmodeltest dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
# DolphinSearchBox
if (Nepomuk_FOUND)
@ -18,20 +36,3 @@ if (Nepomuk_FOUND)
kde4_add_unit_test(dolphinsearchboxtest TEST ${dolphinsearchboxtest_SRCS})
target_link_libraries(dolphinsearchboxtest ${KDE4_KIO_LIBS} ${SOPRANO_LIBRARIES} ${NEPOMUK_LIBRARIES} ${NEPOMUK_QUERY_LIBRARIES} nepomukutils ${QT_QTTEST_LIBRARY})
endif (Nepomuk_FOUND)
# DolphinTreeView
kde4_add_unit_test(dolphintreeviewtest TEST dolphintreeviewtest.cpp)
target_link_libraries(dolphintreeviewtest dolphinprivate ${KDE4_KDEUI_LIBS} ${QT_QTTEST_LIBRARY})
# DolphinView - columns
kde4_add_unit_test(dolphinviewtest_columns TEST dolphinviewtest_columns.cpp dolphinviewtest_allviewmodes.cpp testdir.cpp testbase.cpp ../views/zoomlevelinfo.cpp)
target_link_libraries(dolphinviewtest_columns dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
# DolphinView - details
kde4_add_unit_test(dolphinviewtest_details TEST dolphinviewtest_details.cpp dolphinviewtest_allviewmodes.cpp testdir.cpp testbase.cpp ../views/zoomlevelinfo.cpp)
target_link_libraries(dolphinviewtest_details dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})
# DolphinView - icons
kde4_add_unit_test(dolphinviewtest_icons TEST dolphinviewtest_icons.cpp dolphinviewtest_allviewmodes.cpp testdir.cpp testbase.cpp ../views/zoomlevelinfo.cpp)
target_link_libraries(dolphinviewtest_icons dolphinprivate ${KDE4_KIO_LIBS} ${QT_QTTEST_LIBRARY})

View file

@ -1,304 +0,0 @@
/***************************************************************************
* Copyright (C) 2010 by Frank Reininghaus (frank78ac@googlemail.com) *
* *
* 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 <qtest_kde.h>
#include "testbase.h"
#include "testdir.h"
#include "views/dolphindetailsview.h"
#include "views/dolphinview.h"
#include "views/dolphinmodel.h"
#include "views/dolphinsortfilterproxymodel.h"
#include "views/zoomlevelinfo.h"
#include <qtestmouse.h>
#include <qtestkeyboard.h>
class DolphinDetailsViewTest : public TestBase
{
Q_OBJECT
private slots:
void testExpandedUrls();
void bug217447_shiftArrowSelection();
void bug234600_overlappingIconsWhenZooming();
void bug257401_longFilenamesKeyboardNavigation();
private:
/**
* initView(DolphinView*) sets the correct view mode, shows the view on the screen, and waits
* until loading the folder in the view is finished.
*
* Many unit tests need access to the internal DolphinDetailsView in DolphinView.
* Therefore, a pointer to the details view is returned by initView(DolphinView*).
*/
DolphinDetailsView* initView(DolphinView* view) const {
QSignalSpy spyFinishedPathLoading(view, SIGNAL(finishedPathLoading(const KUrl&)));
view->setMode(DolphinView::DetailsView);
DolphinDetailsView* detailsView = qobject_cast<DolphinDetailsView*>(itemView(view));
Q_ASSERT(detailsView);
detailsView->setFoldersExpandable(true);
view->resize(400, 400);
view->show();
QTest::qWaitForWindowShown(view);
// If the DolphinView's finishedPathLoading(const KUrl&) signal has not been received yet,
// we have to wait a bit more.
// The reason why the if-statement is needed here is that the signal might have been emitted
// while we were waiting in QTest::qWaitForWindowShown(view)
// -> waitForFinishedPathLoading(view) would fail in that case.
if (spyFinishedPathLoading.isEmpty()) {
waitForFinishedPathLoading(view);
}
return detailsView;
}
QModelIndex proxyModelIndexForUrl(const DolphinView* view, const KUrl& url) const {
const QModelIndex index = view->m_viewAccessor.m_dolphinModel->indexForUrl(url);
return view->m_viewAccessor.m_proxyModel->mapFromSource(index);
}
};
/**
* This test verifies that DolphinDetailsView::expandedUrls() returns the right set of URLs.
* The test creates a folder hierarchy: 3 folders (a, b, c) contain 3 subfolders (also named a, b, c) each.
* Each of those contains 3 further subfolders of the same name.
*/
void DolphinDetailsViewTest::testExpandedUrls()
{
QStringList files;
QStringList subFolderNames;
subFolderNames << "a" << "b" << "c";
foreach(const QString& level1, subFolderNames) {
foreach(const QString& level2, subFolderNames) {
foreach(const QString& level3, subFolderNames) {
files << level1 + "/" + level2 + "/" + level3 + "/testfile";
}
}
}
TestDir dir;
dir.createFiles(files);
DolphinView view(dir.url(), 0);
DolphinDetailsView* detailsView = initView(&view);
// We start with an empty set of expanded URLs.
QSet<KUrl> expectedExpandedUrls;
QCOMPARE(detailsView->expandedUrls(), expectedExpandedUrls);
// Expand URLs one by one and verify the result of DolphinDetailsView::expandedUrls()
QStringList itemsToExpand;
itemsToExpand << "b" << "b/a" << "b/a/c" << "b/c" << "c";
foreach(const QString& item, itemsToExpand) {
KUrl url(dir.name() + item);
detailsView->expand(proxyModelIndexForUrl(&view, url));
expectedExpandedUrls += url;
QCOMPARE(detailsView->expandedUrls(), expectedExpandedUrls);
// Before we proceed, we have to make sure that the view has finished
// loading the contents of the expanded folder.
waitForFinishedPathLoading(&view);
}
// Collapse URLs one by one and verify the result of DolphinDetailsView::expandedUrls()
QStringList itemsToCollapse;
itemsToCollapse << "b/c" << "b/a/c" << "c" << "b/a" << "b";
foreach(const QString& item, itemsToCollapse) {
KUrl url(dir.name() + item);
detailsView->collapse(proxyModelIndexForUrl(&view, url));
expectedExpandedUrls -= url;
QCOMPARE(detailsView->expandedUrls(), expectedExpandedUrls);
}
}
/**
* When the first item in the view is active and Shift is held while the "arrow down"
* key is pressed repeatedly, the selection should grow by one item for each key press.
* A change in Qt 4.6 revealed a bug in DolphinDetailsView which broke this, see
*
* https://bugs.kde.org/show_bug.cgi?id=217447
*
* The problem was that DolphinDetailsView, which uses not the full width of the "Name"
* column for an item, but only the width of the actual file name, did not reimplement
* QTreeView::visualRect(). This caused item selection to fail because QAbstractItemView
* uses the center of the visualRect of an item internally. If the width of the file name
* is less than half the width of the "Name" column, the center of an item's visualRect
* was therefore outside the space that DolphinDetailsView actually assigned to the
* item, and this led to unexpected deselection of items.
*
* TODO: To make the test more reliable, one could adjust the width of the "Name"
* column before the test in order to really make sure that the column is more than twice
* as wide as the space actually occupied by the file names (this triggers the bug).
*/
void DolphinDetailsViewTest::bug217447_shiftArrowSelection()
{
TestDir dir;
for (int i = 0; i < 100; i++) {
dir.createFile(QString("%1").arg(i));
}
DolphinView view(dir.url(), 0);
DolphinDetailsView* detailsView = initView(&view);
// Select the first item
QModelIndex index0 = detailsView->model()->index(0, 0);
detailsView->setCurrentIndex(index0);
QCOMPARE(detailsView->currentIndex(), index0);
// Before we test Shift-selection, we verify that the root cause is fixed a bit more
// directly: we check that passing the corners or the center of an item's visualRect
// to itemAt() returns the item (and not an invalid model index).
QRect rect = detailsView->visualRect(index0);
QCOMPARE(detailsView->indexAt(rect.center()), index0);
QCOMPARE(detailsView->indexAt(rect.topLeft()), index0);
QCOMPARE(detailsView->indexAt(rect.topRight()), index0);
QCOMPARE(detailsView->indexAt(rect.bottomLeft()), index0);
QCOMPARE(detailsView->indexAt(rect.bottomRight()), index0);
// Another way to test this is to Ctrl-click the center of the visualRect.
// The selection state of the item should be toggled.
detailsView->clearSelection();
QItemSelectionModel* selectionModel = detailsView->selectionModel();
QCOMPARE(selectionModel->selectedIndexes().count(), 0);
QTest::mouseClick(detailsView->viewport(), Qt::LeftButton, Qt::ControlModifier, rect.center());
QModelIndexList selectedIndexes = selectionModel->selectedIndexes();
QCOMPARE(selectedIndexes.count(), 1);
QVERIFY(selectedIndexes.contains(index0));
// Now we go down item by item using Shift+Down. In each step, we check that the current item
// is added to the selection and that the size of the selection grows by one.
int current = 1;
while (current < 100) {
QTest::keyClick(detailsView->viewport(), Qt::Key_Down, Qt::ShiftModifier);
QModelIndex currentIndex = detailsView->model()->index(current, 0);
QCOMPARE(detailsView->currentIndex(), currentIndex);
selectedIndexes = selectionModel->selectedIndexes();
QCOMPARE(selectedIndexes.count(), current + 1);
QVERIFY(selectedIndexes.contains(currentIndex));
current++;
}
}
/**
* When the icon size is changed, we have to make sure that the maximumSize given
* to KFileItemDelegate for rendering each item is updated correctly. If this is not
* done, the visualRects are clipped by the incorrect maximum size, and the icons
* may overlap, see
*
* https://bugs.kde.org/show_bug.cgi?id=234600
*/
void DolphinDetailsViewTest::bug234600_overlappingIconsWhenZooming()
{
QStringList files;
files << "a" << "b" << "c" << "d";
TestDir dir;
dir.createFiles(files);
DolphinView view(dir.url(), 0);
DolphinDetailsView* detailsView = initView(&view);
QModelIndex index0 = detailsView->model()->index(0, 0);
detailsView->setCurrentIndex(index0);
QCOMPARE(detailsView->currentIndex(), index0);
// Setting the zoom level to the minimum value and triggering DolphinDetailsView::currentChanged(...)
// should make sure that the bug is triggered.
int zoomLevelBackup = view.zoomLevel();
int zoomLevel = ZoomLevelInfo::minimumLevel();
view.setZoomLevel(zoomLevel);
QModelIndex index1 = detailsView->model()->index(1, 0);
detailsView->setCurrentIndex(index1);
QCOMPARE(detailsView->currentIndex(), index1);
// Increase the zoom level successively to the maximum.
while(zoomLevel < ZoomLevelInfo::maximumLevel()) {
zoomLevel++;
view.setZoomLevel(zoomLevel);
QCOMPARE(view.zoomLevel(), zoomLevel);
//Check for each zoom level that the height of each item is at least the icon size.
QVERIFY(detailsView->visualRect(index1).height() >= ZoomLevelInfo::iconSizeForZoomLevel(zoomLevel));
}
view.setZoomLevel(zoomLevelBackup);
}
/**
* The width of the visualRect of an item is usually replaced by the width of the file name.
* However, if the file name is wider then the view's name column, this leads to problems with
* keyboard navigation if files with very long names are present in the current folder, see
*
* https://bugs.kde.org/show_bug.cgi?id=257401
*
* This test checks that the visualRect of an item is never wider than the "Name" column.
*/
void DolphinDetailsViewTest::bug257401_longFilenamesKeyboardNavigation() {
TestDir dir;
QString name;
for (int i = 0; i < 20; i++) {
name += "mmmmmmmmmm";
dir.createFile(name);
}
DolphinView view(dir.url(), 0);
DolphinDetailsView* detailsView = initView(&view);
// Select the first item
QModelIndex index0 = detailsView->model()->index(0, 0);
detailsView->setCurrentIndex(index0);
QCOMPARE(detailsView->currentIndex(), index0);
QVERIFY(detailsView->visualRect(index0).width() < detailsView->columnWidth(DolphinModel::Name));
QItemSelectionModel* selectionModel = detailsView->selectionModel();
QModelIndexList selectedIndexes = selectionModel->selectedIndexes();
QCOMPARE(selectedIndexes.count(), 1);
QVERIFY(selectedIndexes.contains(index0));
// Move down successively using the "Down" key and check that current item
// and selection are as expected.
for (int i = 0; i < 19; i++) {
QTest::keyClick(detailsView->viewport(), Qt::Key_Down, Qt::NoModifier);
QModelIndex currentIndex = detailsView->model()->index(i + 1, 0);
QCOMPARE(detailsView->currentIndex(), currentIndex);
QVERIFY(detailsView->visualRect(currentIndex).width() <= detailsView->columnWidth(DolphinModel::Name));
selectedIndexes = selectionModel->selectedIndexes();
QCOMPARE(selectedIndexes.count(), 1);
QVERIFY(selectedIndexes.contains(currentIndex));
}
}
QTEST_KDEMAIN(DolphinDetailsViewTest, GUI)
#include "dolphindetailsviewtest.moc"

View file

@ -1,413 +0,0 @@
/*****************************************************************************
* Copyright (C) 2010-2011 by Frank Reininghaus (frank78ac@googlemail.com) *
* *
* 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 <qtest_kde.h>
#include <KDebug>
#include <KAction>
#include "views/dolphintreeview.h"
#include <qtestkeyboard.h>
#include <qtestmouse.h>
#include <QStringListModel>
class DolphinTreeViewTest : public QObject
{
Q_OBJECT
private slots:
void testKeyboardNavigationSelectionUpdate();
void bug218114_visualRegionForSelection();
void bug220898_focusOut();
private:
/** A method that simplifies checking a view's current item and selection */
static void verifyCurrentItemAndSelection(const QAbstractItemView& view, const QModelIndex& expectedCurrent, const QModelIndexList& expectedSelection) {
QCOMPARE(view.currentIndex(), expectedCurrent);
const QModelIndexList selectedIndexes = view.selectionModel()->selectedIndexes();
QCOMPARE(selectedIndexes.count(), expectedSelection.count());
foreach(const QModelIndex& index, expectedSelection) {
QVERIFY(selectedIndexes.contains(index));
}
}
/** Use this method if only one item is selected */
static void verifyCurrentItemAndSelection(const QAbstractItemView& view, const QModelIndex& current, const QModelIndex& selected) {
QModelIndexList list;
list << selected;
verifyCurrentItemAndSelection(view, current, list);
}
/** Use this method if the only selected item is the current item */
static void verifyCurrentItemAndSelection(const QAbstractItemView& view, const QModelIndex& current) {
verifyCurrentItemAndSelection(view, current, current);
}
};
/**
* TestView is a simple view class derived from DolphinTreeView.
* It makes sure that the visualRect for each index contains only the item text as
* returned by QAbstractItemModel::data(...) for the role Qt::DisplayRole.
*
* We have to check that DolphinTreeView handles the case of visualRects with different widths
* correctly because this is the case in DolphinDetailsView which is derived from DolphinTreeView.
*/
class TestView : public DolphinTreeView
{
Q_OBJECT
public:
TestView(QWidget* parent = 0) : DolphinTreeView(parent) {};
~TestView() {};
QRect visualRect(const QModelIndex& index) const {
QRect rect = DolphinTreeView::visualRect(index);
const QStyleOptionViewItem option = viewOptions();
const QFontMetrics fontMetrics(option.font);
int width = option.decorationSize.width() + fontMetrics.width(model()->data(index).toString());
rect.setWidth(width);
return rect;
}
};
/**
* This test checks that updating the selection after key presses works as expected.
* Qt does not handle this internally if the first letter of an item is pressed, which
* is why DolphinTreeView has some custom code for this. The test verifies that this
* works without unwanted side effects.
*
* The test uses the class TreeViewWithDeleteShortcut which deletes the selected items
* when Shift-Delete is pressed. This is needed to test the fix for bug 259656 (see below).
*/
class TreeViewWithDeleteShortcut : public DolphinTreeView {
Q_OBJECT
public:
TreeViewWithDeleteShortcut(QWidget* parent = 0) : DolphinTreeView(parent) {
// To test the fix for bug 259656, we need a delete shortcut.
KAction* deleteAction = new KAction(this);
deleteAction->setShortcut(Qt::SHIFT | Qt::Key_Delete);
addAction(deleteAction);
connect(deleteAction, SIGNAL(triggered()), this, SLOT(deleteSelectedItems()));
};
~TreeViewWithDeleteShortcut() {};
public slots:
void deleteSelectedItems() {
// We have to delete the items one by one and update the list of selected items after
// each step because every removal will invalidate the model indexes in the list.
QModelIndexList selectedItems = selectionModel()->selectedIndexes();
while (!selectedItems.isEmpty()) {
const QModelIndex index = selectedItems.takeFirst();
model()->removeRow(index.row());
selectedItems = selectionModel()->selectedIndexes();
}
}
};
void DolphinTreeViewTest::testKeyboardNavigationSelectionUpdate() {
QStringList items;
items << "a" << "b" << "c" << "d" << "e";
QStringListModel model(items);
QModelIndex index[5];
for (int i = 0; i < 5; i++) {
index[i] = model.index(i, 0);
}
TreeViewWithDeleteShortcut view;
view.setModel(&model);
view.setSelectionMode(QAbstractItemView::ExtendedSelection);
view.resize(400, 400);
view.show();
QTest::qWaitForWindowShown(&view);
view.clearSelection();
QVERIFY(view.selectionModel()->selectedIndexes().isEmpty());
/**
* Check that basic keyboard navigation with arrow keys works.
*/
view.setCurrentIndex(index[0]);
verifyCurrentItemAndSelection(view, index[0]);
// Go down -> item 1 ("b") should be selected
kDebug() << "Down";
QTest::keyClick(view.viewport(), Qt::Key_Down);
verifyCurrentItemAndSelection(view, index[1]);
// Go down -> item 2 ("c") should be selected
kDebug() << "Down";
QTest::keyClick(view.viewport(), Qt::Key_Down);
verifyCurrentItemAndSelection(view, index[2]);
// Ctrl-Up -> item 2 ("c") remains selected
kDebug() << "Ctrl-Up";
QTest::keyClick(view.viewport(), Qt::Key_Up, Qt::ControlModifier);
verifyCurrentItemAndSelection(view, index[1], index[2]);
// Go up -> item 0 ("a") should be selected
kDebug() << "Up";
QTest::keyClick(view.viewport(), Qt::Key_Up);
verifyCurrentItemAndSelection(view, index[0]);
// Shift-Down -> items 0 and 1 ("a" and "b") should be selected
kDebug() << "Shift-Down";
QTest::keyClick(view.viewport(), Qt::Key_Down, Qt::ShiftModifier);
QModelIndexList expectedSelection;
expectedSelection << index[0] << index[1];
verifyCurrentItemAndSelection(view, index[1], expectedSelection);
/**
* When the first letter of a file name is pressed, this file becomes the current item
* and gets selected. If the user then Shift-clicks another item, it is expected that
* all items between these two items get selected. Before the bug
*
* https://bugs.kde.org/show_bug.cgi?id=201459
*
* was fixed, this was not the case: the starting point for the Shift-selection was not
* updated if an item was selected by pressing the first letter of the file name.
*/
view.clearSelection();
QVERIFY(view.selectionModel()->selectedIndexes().isEmpty());
// Control-click item 0 ("a")
kDebug() << "Ctrl-click on \"a\"";
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, view.visualRect(index[0]).center());
verifyCurrentItemAndSelection(view, index[0]);
// Press "c", such that item 2 ("c") should be the current one.
kDebug() << "Press \"c\"";
QTest::keyClick(view.viewport(), Qt::Key_C);
verifyCurrentItemAndSelection(view, index[2]);
// Now Shift-Click the last item ("e"). We expect that 3 items ("c", "d", "e") are selected.
kDebug() << "Shift-click on \"e\"";
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, view.visualRect(index[4]).center());
expectedSelection.clear();
expectedSelection << index[2] << index[3] << index[4];
verifyCurrentItemAndSelection(view, index[4], expectedSelection);
/**
* Starting a drag&drop operation should not clear the selection, see
*
* https://bugs.kde.org/show_bug.cgi?id=158649
*/
view.clearSelection();
QVERIFY(view.selectionModel()->selectedIndexes().isEmpty());
// Click item 0 ("a")
kDebug() << "Click on \"a\"";
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(index[0]).center());
verifyCurrentItemAndSelection(view, index[0]);
// Shift-Down -> "a" and "b" should be selected
kDebug() << "Shift-Down";
QTest::keyClick(view.viewport(), Qt::Key_Down, Qt::ShiftModifier);
expectedSelection.clear();
expectedSelection << index[0] << index[1];
verifyCurrentItemAndSelection(view, index[1], expectedSelection);
// Press mouse button on item 0 ("a"), but do not release it. Check that the selection is unchanged
kDebug() << "Mouse press on \"a\"";
QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(index[0]).center());
verifyCurrentItemAndSelection(view, index[0], expectedSelection);
// Move mouse to item 1 ("b"), check that selection is unchanged
kDebug() << "Move mouse to \"b\"";
QMouseEvent moveEvent(QEvent::MouseMove, view.visualRect(index[1]).center(), Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent);
QVERIFY(moveEventReceived);
verifyCurrentItemAndSelection(view, index[0], expectedSelection);
// Release mouse button on item 1 ("b"), check that selection is unchanged
kDebug() << "Mouse release on \"b\"";
QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(index[1]).center());
verifyCurrentItemAndSelection(view, index[0], expectedSelection);
/**
* Keeping Shift+Delete pressed for some time should delete only one item, see
*
* https://bugs.kde.org/show_bug.cgi?id=259656
*/
view.clearSelection();
QVERIFY(view.selectionModel()->selectedIndexes().isEmpty());
// Click item 0 ("a")
kDebug() << "Click on \"a\"";
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(index[0]).center());
verifyCurrentItemAndSelection(view, index[0]);
// Press Shift-Delete and keep the keys pressed for some time
kDebug() << "Press Shift-Delete";
QTest::keyPress(view.viewport(), Qt::Key_Delete, Qt::ShiftModifier);
QTest::qWait(200);
QTest::keyRelease(view.viewport(), Qt::Key_Delete, Qt::ShiftModifier);
// Verify that only one item has been deleted
QCOMPARE(view.model()->rowCount(), 4);
}
/**
* QTreeView assumes implicitly that the width of each item's visualRect is the same. This leads to painting
* problems in Dolphin if items with different widths are in one QItemSelectionRange, see
*
* https://bugs.kde.org/show_bug.cgi?id=218114
*
* To fix this, DolphinTreeView has a custom implementation of visualRegionForSelection(). The following
* unit test checks that.
*/
void DolphinTreeViewTest::bug218114_visualRegionForSelection()
{
QStringList items;
items << "a" << "an item with a long name" << "a";
QStringListModel model(items);
QModelIndex index0 = model.index(0, 0);
QModelIndex index1 = model.index(1, 0);
QModelIndex index2 = model.index(2, 0);
TestView view;
view.setModel(&model);
view.setSelectionMode(QAbstractItemView::ExtendedSelection);
view.resize(400, 400);
view.show();
QTest::qWaitForWindowShown(&view);
// First check that the width of index1 is larger than that of index0 and index2 (this triggers the bug).
QVERIFY(view.visualRect(index0).width() < view.visualRect(index1).width());
QVERIFY(view.visualRect(index2).width() < view.visualRect(index1).width());
// Select all items in one go.
view.selectAll();
const QItemSelection selection = view.selectionModel()->selection();
QCOMPARE(selection.count(), 1);
QCOMPARE(selection.indexes().count(), 3);
// Verify that the visualRegionForSelection contains all visualRects.
// We do this indirectly using QRegion::boundingRect() because
// QRegion::contains(const QRect&) returns true even if the QRect is not
// entirely inside the QRegion.
const QRegion region = view.visualRegionForSelection(selection);
const QRect boundingRect = region.boundingRect();
QVERIFY(boundingRect.contains(view.visualRect(index0)));
QVERIFY(boundingRect.contains(view.visualRect(index1)));
QVERIFY(boundingRect.contains(view.visualRect(index2)));
}
/**
* This test verifies that selection of multiple items with the mouse works
* if a key was pressed and the keyboard focus moved to another window before the
* key was released, see
*
* https://bugs.kde.org/show_bug.cgi?id=220898
*/
void DolphinTreeViewTest::bug220898_focusOut()
{
QStringList items;
items << "a" << "b" << "c" << "d" << "e";
QStringListModel model(items);
QModelIndex index[5];
for (int i = 0; i < 5; i++) {
index[i] = model.index(i, 0);
}
TestView view;
view.setModel(&model);
view.setSelectionMode(QAbstractItemView::ExtendedSelection);
view.resize(400, 400);
view.show();
QTest::qWaitForWindowShown(&view);
view.setCurrentIndex(index[0]);
verifyCurrentItemAndSelection(view, index[0]);
// Press Down
QTest::keyPress(view.viewport(), Qt::Key_Down, Qt::NoModifier);
// Move keyboard focus to another widget
QWidget widget;
widget.show();
QTest::qWaitForWindowShown(&widget);
widget.setFocus();
// Wait until the widgets have received the focus events
while (view.viewport()->hasFocus()) {
QTest::qWait(10);
}
QVERIFY(!view.viewport()->hasFocus());
// Release the "Down" key
QTest::keyRelease(&widget, Qt::Key_Down, Qt::NoModifier);
// Move keyboard focus back to the view
widget.hide();
view.viewport()->setFocus();
// Wait until the widgets have received the focus events
while (widget.hasFocus()) {
QTest::qWait(10);
}
QVERIFY(!widget.hasFocus());
// Press left mouse button below the last item
const int lastRowHeight = view.sizeHintForRow(4);
QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(index[4]).center() + QPoint(0, lastRowHeight));
// Move mouse to the first item and release
QTest::mouseMove(view.viewport(), view.visualRect(index[0]).center());
QMouseEvent moveEvent(QEvent::MouseMove, view.visualRect(index[0]).center(), Qt::NoButton, Qt::LeftButton, Qt::NoModifier);
bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent);
QVERIFY(moveEventReceived);
QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(index[0]).center());
// All items should be selected
QModelIndexList expectedSelection;
expectedSelection << index[0] << index[1] << index[2] << index[3] << index[4];
verifyCurrentItemAndSelection(view, index[0], expectedSelection);
}
QTEST_KDEMAIN(DolphinTreeViewTest, GUI)
#include "dolphintreeviewtest.moc"

View file

@ -1,596 +0,0 @@
/****************************************************************************
* Copyright (C) 2010-2011 by Frank Reininghaus (frank78ac@googlemail.com) *
* *
* 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 <kdebug.h>
#include "dolphinviewtest_allviewmodes.h"
#include <qtest_kde.h>
#include "testdir.h"
#include "views/dolphinview.h"
#include "views/dolphinmodel.h"
#include "views/dolphindirlister.h"
#include "views/dolphinsortfilterproxymodel.h"
#include "views/zoomlevelinfo.h"
#include <QScrollBar>
#include <qtestmouse.h>
#include <qtestkeyboard.h>
DolphinViewTest_AllViewModes::DolphinViewTest_AllViewModes() {
// Need to register KFileItemList for use with QSignalSpy
qRegisterMetaType<KFileItemList>("KFileItemList");
}
/**
* testSelection() checks the basic selection functionality of DolphinView, including:
*
* selectedItems()
* selectedItemsCount()
* selectAll()
* invertSelection()
* clearSelection()
* hasSelection()
*
* and the signal selectionChanged(const KFileItemList& selection)
*/
Q_DECLARE_METATYPE(KFileItemList)
void DolphinViewTest_AllViewModes::testSelection() {
TestDir dir;
const int totalItems = 50;
for (int i = 0; i < totalItems; i++) {
dir.createFile(QString("%1").arg(i));
}
DolphinView view(dir.url(), 0);
QAbstractItemView* itemView = initView(&view);
// Start with an empty selection
view.clearSelection();
QCOMPARE(view.selectedItems().count(), 0);
QCOMPARE(view.selectedItemsCount(), 0);
QVERIFY(!view.hasSelection());
// First some simple tests where either all or no items are selected
view.selectAll();
verifySelectedItemsCount(&view, totalItems);
view.invertSelection();
verifySelectedItemsCount(&view, 0);
view.invertSelection();
verifySelectedItemsCount(&view, totalItems);
view.clearSelection();
verifySelectedItemsCount(&view, 0);
// Now we select individual items using mouse clicks
QModelIndex index = itemView->model()->index(2, 0);
itemView->scrollTo(index);
QTest::mouseClick(itemView->viewport(), Qt::LeftButton, Qt::ControlModifier, itemView->visualRect(index).center());
verifySelectedItemsCount(&view, 1);
index = itemView->model()->index(totalItems - 5, 0);
itemView->scrollTo(index);
QTest::mouseClick(itemView->viewport(), Qt::LeftButton, Qt::ControlModifier, itemView->visualRect(index).center());
verifySelectedItemsCount(&view, 2);
index = itemView->model()->index(totalItems - 2, 0);
itemView->scrollTo(index);
QTest::mouseClick(itemView->viewport(), Qt::LeftButton, Qt::ShiftModifier, itemView->visualRect(index).center());
verifySelectedItemsCount(&view, 5);
view.invertSelection();
verifySelectedItemsCount(&view, totalItems - 5);
// Pressing Esc should clear the selection
QTest::keyClick(itemView->viewport(), Qt::Key_Escape);
verifySelectedItemsCount(&view, 0);
}
/**
* Check that setting the directory view properties works.
*/
void DolphinViewTest_AllViewModes::testViewPropertySettings()
{
// Create some files with different sizes and modification times to check the different sorting options
QDateTime now = QDateTime::currentDateTime();
TestDir dir;
dir.createFile("a", "A file", now.addDays(-3));
dir.createFile("b", "A larger file", now.addDays(0));
dir.createDir("c", now.addDays(-2));
dir.createFile("d", "The largest file in this directory", now.addDays(-1));
dir.createFile("e", "An even larger file", now.addDays(-4));
dir.createFile(".f");
DolphinView view(dir.url(), 0);
initView(&view);
// First set all settings to the default.
view.setSorting(DolphinView::SortByName);
QCOMPARE(view.sorting(), DolphinView::SortByName);
view.setSortOrder(Qt::AscendingOrder);
QCOMPARE(view.sortOrder(), Qt::AscendingOrder);
view.setSortFoldersFirst(true);
QVERIFY(view.sortFoldersFirst());
view.setShowPreview(false);
QVERIFY(!view.showPreview());
if (view.showHiddenFiles()) {
// Changing the "hidden files" setting triggers the dir lister
// -> we have to wait until loading the hidden files is finished
view.setShowHiddenFiles(false);
waitForFinishedPathLoading(&view);
}
QVERIFY(!view.showHiddenFiles());
/** Check that the sort order is correct for different kinds of settings */
// Sort by Name, ascending
QCOMPARE(view.sorting(), DolphinView::SortByName);
QCOMPARE(view.sortOrder(), Qt::AscendingOrder);
QCOMPARE(viewItems(&view), QStringList() << "c" << "a" << "b" << "d" << "e");
// Sort by Name, descending
view.setSortOrder(Qt::DescendingOrder);
QCOMPARE(view.sorting(), DolphinView::SortByName);
QCOMPARE(view.sortOrder(), Qt::DescendingOrder);
QCOMPARE(viewItems(&view), QStringList() << "c" << "e" << "d" << "b" << "a");
// Sort by Size, descending
view.setSorting(DolphinView::SortBySize);
QCOMPARE(view.sorting(), DolphinView::SortBySize);
QCOMPARE(view.sortOrder(), Qt::DescendingOrder);
QCOMPARE(viewItems(&view), QStringList() << "c" << "d" << "e" << "b" << "a");
// Sort by Size, ascending
view.setSortOrder(Qt::AscendingOrder);
QCOMPARE(view.sorting(), DolphinView::SortBySize);
QCOMPARE(view.sortOrder(), Qt::AscendingOrder);
QCOMPARE(viewItems(&view), QStringList() << "c" << "a" << "b" << "e" << "d");
// Sort by Date, ascending
view.setSorting(DolphinView::SortByDate);
QCOMPARE(view.sorting(), DolphinView::SortByDate);
QCOMPARE(view.sortOrder(), Qt::AscendingOrder);
QCOMPARE(viewItems(&view), QStringList() << "c" << "e" << "a" << "d" << "b");
// Sort by Date, descending
view.setSortOrder(Qt::DescendingOrder);
QCOMPARE(view.sorting(), DolphinView::SortByDate);
QCOMPARE(view.sortOrder(), Qt::DescendingOrder);
QCOMPARE(viewItems(&view), QStringList() << "c" << "b" << "d" << "a" << "e");
// Disable "Sort Folders First"
view.setSortFoldersFirst(false);
QVERIFY(!view.sortFoldersFirst());
QCOMPARE(viewItems(&view), QStringList()<< "b" << "d" << "c" << "a" << "e");
// Try again with Sort by Name, ascending
view.setSorting(DolphinView::SortByName);
view.setSortOrder(Qt::AscendingOrder);
QCOMPARE(view.sorting(), DolphinView::SortByName);
QCOMPARE(view.sortOrder(), Qt::AscendingOrder);
QCOMPARE(viewItems(&view), QStringList() << "a" << "b" << "c" << "d" << "e");
// Show hidden files. This triggers the dir lister
// -> we have to wait until loading the hidden files is finished
view.setShowHiddenFiles(true);
waitForFinishedPathLoading(&view);
QVERIFY(view.showHiddenFiles());
// Depending on the settings, a .directory file might have been created.
// Remove it from the list to get consistent results.
QStringList result = viewItems(&view);
result.removeAll(".directory");
QCOMPARE(result, QStringList() << ".f" << "a" << "b" << "c" << "d" << "e");
// Previews
view.setShowPreview(true);
QVERIFY(view.showPreview());
// TODO: Check that the view properties are restored correctly when changing the folder and then going back.
}
/**
* testZoomLevel() checks that setting the zoom level works, both using DolphinView's API and using Ctrl+mouse wheel.
*/
void DolphinViewTest_AllViewModes::testZoomLevel()
{
TestDir dir;
dir.createFiles(QStringList() << "a" << "b");
DolphinView view(dir.url(), 0);
QAbstractItemView* itemView = initView(&view);
view.setShowPreview(false);
QVERIFY(!view.showPreview());
int zoomLevelBackup = view.zoomLevel();
int zoomLevel = ZoomLevelInfo::minimumLevel();
view.setZoomLevel(zoomLevel);
QCOMPARE(view.zoomLevel(), zoomLevel);
// Increase the zoom level successively to the maximum.
while(zoomLevel < ZoomLevelInfo::maximumLevel()) {
zoomLevel++;
view.setZoomLevel(zoomLevel);
QCOMPARE(view.zoomLevel(), zoomLevel);
}
// Try setting a zoom level larger than the maximum
view.setZoomLevel(ZoomLevelInfo::maximumLevel() + 1);
QCOMPARE(view.zoomLevel(), ZoomLevelInfo::maximumLevel());
// Turn previews on and try setting a zoom level smaller than the minimum
view.setShowPreview(true);
QVERIFY(view.showPreview());
view.setZoomLevel(ZoomLevelInfo::minimumLevel() - 1);
QCOMPARE(view.zoomLevel(), ZoomLevelInfo::minimumLevel());
// Turn previews off again and check that the zoom level is restored
view.setShowPreview(false);
QVERIFY(!view.showPreview());
QCOMPARE(view.zoomLevel(), ZoomLevelInfo::maximumLevel());
// Change the zoom level using Ctrl+mouse wheel
QModelIndex index = itemView->model()->index(0, 0);
itemView->scrollTo(index);
while (view.zoomLevel() > ZoomLevelInfo::minimumLevel()) {
int oldZoomLevel = view.zoomLevel();
QWheelEvent wheelEvent(itemView->visualRect(index).center(), -1, Qt::NoButton, Qt::ControlModifier);
bool wheelEventReceived = qApp->notify(itemView->viewport(), &wheelEvent);
QVERIFY(wheelEventReceived);
QVERIFY(view.zoomLevel() < oldZoomLevel);
}
QCOMPARE(view.zoomLevel(), ZoomLevelInfo::minimumLevel());
while (view.zoomLevel() < ZoomLevelInfo::maximumLevel()) {
int oldZoomLevel = view.zoomLevel();
QWheelEvent wheelEvent(itemView->visualRect(index).center(), 1, Qt::NoButton, Qt::ControlModifier);
bool wheelEventReceived = qApp->notify(itemView->viewport(), &wheelEvent);
QVERIFY(wheelEventReceived);
QVERIFY(view.zoomLevel() > oldZoomLevel);
}
QCOMPARE(view.zoomLevel(), ZoomLevelInfo::maximumLevel());
// Turn previews on again and check that the zoom level is restored
view.setShowPreview(true);
QVERIFY(view.showPreview());
QCOMPARE(view.zoomLevel(), ZoomLevelInfo::minimumLevel());
// Restore the initial state
view.setZoomLevel(zoomLevelBackup);
view.setShowPreview(false);
view.setZoomLevel(zoomLevelBackup);
}
/**
* testSaveAndRestoreState() checks if saving and restoring the view state (current item, scroll position).
*
* Note that we call qApp->sendPostedEvents() every time the view's finishedPathLoading(const KUrl&) signal
* is received. The reason is that the scroll position is restored in the slot restoreContentsPosition(),
* which is been invoked using a queued connection in DolphinView::slotLoadingCompleted(). To make sure
* that this slot is really executed before we proceed, we have to empty the event queue using qApp->sendPostedEvents().
*/
void DolphinViewTest_AllViewModes::testSaveAndRestoreState()
{
const int totalItems = 50;
TestDir dir;
for (int i = 0; i < totalItems; i++) {
dir.createFile(QString("%1").arg(i));
}
dir.createDir("51");
DolphinView view(dir.url(), 0);
initView(&view);
// Set sorting settings to the default to make sure that the item positions are reproducible.
view.setSorting(DolphinView::SortByName);
QCOMPARE(view.sorting(), DolphinView::SortByName);
view.setSortOrder(Qt::AscendingOrder);
QCOMPARE(view.sortOrder(), Qt::AscendingOrder);
// Make sure that previews are off and that the icon size does not depend on the preview setting.
// This is needed for the test for bug 270437, see below.
view.setShowPreview(false);
int zoomLevel = view.zoomLevel();
view.setShowPreview(true);
view.setZoomLevel(zoomLevel);
view.setShowPreview(false);
// Select item 45
const QModelIndex index45 = itemView(&view)->model()->index(45, 0);
itemView(&view)->scrollTo(index45);
itemView(&view)->setCurrentIndex(index45);
const int scrollPosX = itemView(&view)->horizontalScrollBar()->value();
const int scrollPosY = itemView(&view)->verticalScrollBar()->value();
// Save the view state
QByteArray viewState;
QDataStream saveStream(&viewState, QIODevice::WriteOnly);
view.saveState(saveStream);
// Change the URL
view.setUrl(KUrl(dir.name() + "51"));
waitForFinishedPathLoading(&view);
qApp->sendPostedEvents();
// Go back, but do not call DolphinView::restoreState()
view.setUrl(dir.url());
waitForFinishedPathLoading(&view);
qApp->sendPostedEvents();
// Verify that the view is scrolled to top-left corner and that item 45 is not the current item.
// Note that the vertical position of the columns view might not be zero -> skip that part
// of the check in this case.
QVERIFY(itemView(&view)->currentIndex() != index45);
QCOMPARE(itemView(&view)->horizontalScrollBar()->value(), 0);
if (mode() != DolphinView::ColumnView) {
QCOMPARE(itemView(&view)->verticalScrollBar()->value(), 0);
}
// Change the URL again
view.setUrl(KUrl(dir.name() + "51"));
waitForFinishedPathLoading(&view);
qApp->sendPostedEvents();
// Check that the current item and scroll position are correct if DolphinView::restoreState()
// is called after the URL change
view.setUrl(dir.url());
QDataStream restoreStream(viewState);
view.restoreState(restoreStream);
waitForFinishedPathLoading(&view);
qApp->sendPostedEvents();
QCOMPARE(itemView(&view)->currentIndex(), index45);
QCOMPARE(itemView(&view)->horizontalScrollBar()->value(), scrollPosX);
QCOMPARE(itemView(&view)->verticalScrollBar()->value(), scrollPosY);
/**
* Additionally, we verify the fix for the bug https://bugs.kde.org/show_bug.cgi?id=270437
* Actually, it's a bug in KFilePreviewGenerator, but it is easier to test it here.
*/
// Turn previews on.
view.setShowPreview(true);
QVERIFY(view.showPreview());
// We have to process all events in the queue to make sure that previews are really on.
qApp->sendPostedEvents();
// Current item and scroll position should not change.
QCOMPARE(itemView(&view)->currentIndex(), index45);
QCOMPARE(itemView(&view)->horizontalScrollBar()->value(), scrollPosX);
QCOMPARE(itemView(&view)->verticalScrollBar()->value(), scrollPosY);
// Turn previews off again. Before bug 270437, this triggered the dir lister's openUrl() method
// -> we check that by listening to the view's startedPathLoading() signal and wait until the loading is finished in that case.
QSignalSpy spy(&view, SIGNAL(startedPathLoading(const KUrl&)));
view.setShowPreview(false);
QVERIFY(!view.showPreview());
qApp->sendPostedEvents();
if (!spy.isEmpty()) {
// The dir lister reloads the directory. We wait until the loading is finished.
waitForFinishedPathLoading(&view);
}
// Current item and scroll position should not change.
QCOMPARE(itemView(&view)->currentIndex(), index45);
QCOMPARE(itemView(&view)->horizontalScrollBar()->value(), scrollPosX);
QCOMPARE(itemView(&view)->verticalScrollBar()->value(), scrollPosY);
}
/**
* testKeyboardFocus() checks whether a view grabs the keyboard focus.
*
* A view may never grab the keyboard focus itself and must respect the focus-state
* when switching the view mode, see
*
* https://bugs.kde.org/show_bug.cgi?id=261147
*/
void DolphinViewTest_AllViewModes::testKeyboardFocus()
{
TestDir dir;
dir.createFiles(QStringList() << "a" << "b");
DolphinView view(dir.url(), 0);
initView(&view);
// Move the keyboard focus to another widget.
QWidget widget;
widget.show();
QTest::qWaitForWindowShown(&widget);
widget.setFocus();
QVERIFY(!view.hasFocus());
// Switch view modes and verify that the view does not get the focus back
for (int i = 0; i <= DolphinView::MaxModeEnum; ++i) {
view.setMode(static_cast<DolphinView::Mode>(i));
QVERIFY(!view.hasFocus());
}
}
/**
* testCutCopyPaste() checks if cutting or copying items in one view and pasting
* them in another one works.
*/
void DolphinViewTest_AllViewModes::testCutCopyPaste()
{
TestDir dir1;
dir1.createFiles(QStringList() << "a" << "b" << "c" << "d");
DolphinView view1(dir1.url(), 0);
QAbstractItemView* itemView1 = initView(&view1);
TestDir dir2;
dir2.createFiles(QStringList() << "1" << "2" << "3" << "4");
dir2.createDir("subfolder");
DolphinView view2(dir2.url(), 0);
QAbstractItemView* itemView2 = initView(&view2);
// Make sure that both views are sorted by name in ascending order
// TODO: Maybe that should be done in initView(), such all tests can rely on it...?
view1.setSorting(DolphinView::SortByName);
view1.setSortOrder(Qt::AscendingOrder);
view2.setSorting(DolphinView::SortByName);
view2.setSortOrder(Qt::AscendingOrder);
view2.setSortFoldersFirst(true);
QCOMPARE(viewItems(&view1), QStringList() << "a" << "b" << "c" << "d");
QCOMPARE(viewItems(&view2), QStringList() << "subfolder" << "1" << "2" << "3" << "4");
/** Copy and paste */
// Select an item ("d") n view1, copy it and paste it in view2.
// Note that we have to wait for view2's finishedPathLoading() signal because the pasting is done in the background.
QModelIndex index = itemView1->model()->index(3, 0);
itemView1->scrollTo(index);
QTest::mouseClick(itemView1->viewport(), Qt::LeftButton, Qt::ControlModifier, itemView1->visualRect(index).center());
verifySelectedItemsCount(&view1, 1);
QCOMPARE(selectedItems(&view1), QStringList() << "d");
view1.copySelectedItems();
view2.paste();
waitForFinishedPathLoading(&view2);
QCOMPARE(viewItems(&view1), QStringList() << "a" << "b" << "c" << "d");
QCOMPARE(viewItems(&view2), QStringList() << "subfolder" << "1" << "2" << "3" << "4" << "d");
// The pasted item should be selected
QCOMPARE(selectedItems(&view2), QStringList() << "d");
/** Cut and paste */
// Select two items ("3", "4") in view2, cut and paste in view1.
view2.clearSelection();
index = itemView2->model()->index(3, 0);
itemView2->scrollTo(index);
QTest::mouseClick(itemView2->viewport(), Qt::LeftButton, Qt::ControlModifier, itemView2->visualRect(index).center());
verifySelectedItemsCount(&view2, 1);
index = itemView2->model()->index(4, 0);
itemView2->scrollTo(index);
QTest::mouseClick(itemView2->viewport(), Qt::LeftButton, Qt::ShiftModifier, itemView2->visualRect(index).center());
verifySelectedItemsCount(&view2, 2);
QCOMPARE(selectedItems(&view2), QStringList() << "3" << "4");
view2.cutSelectedItems();
// In view1, "d" is still selected
QCOMPARE(selectedItems(&view1), QStringList() << "d");
// Paste "3" and "4"
view1.paste();
waitForFinishedPathLoading(&view1);
// In principle, KIO could implement copy&paste such that the pasted items are already there, but the cut items
// have not been removed yet. Therefore, we check the number of items in view2 and also wait for that view's
// finishedPathLoading() signal if the cut items are still there.
if (viewItems(&view2).count() > 4) {
waitForFinishedPathLoading(&view2);
}
QCOMPARE(viewItems(&view1), QStringList() << "3" << "4" << "a" << "b" << "c" << "d");
QCOMPARE(viewItems(&view2), QStringList() << "subfolder" << "1" << "2" << "d");
// The pasted items ("3", "4") should be selected now, and the previous selection ("d") should be cleared.
QCOMPARE(selectedItems(&view1), QStringList() << "3" << "4");
/** Copy and paste into subfolder */
view1.clearSelection();
index = itemView1->model()->index(3, 0);
itemView1->scrollTo(index);
QTest::mouseClick(itemView1->viewport(), Qt::LeftButton, Qt::ControlModifier, itemView1->visualRect(index).center());
verifySelectedItemsCount(&view1, 1);
QCOMPARE(selectedItems(&view1), QStringList() << "b");
view1.copySelectedItems();
// Now we use view1 to display the subfolder, which is still empty.
view1.setUrl(KUrl(dir2.name() + "subfolder"));
waitForFinishedPathLoading(&view1);
QCOMPARE(viewItems(&view1), QStringList());
// Select the subfolder.in view2
view2.clearSelection();
index = itemView2->model()->index(0, 0);
itemView2->scrollTo(index);
QTest::mouseClick(itemView2->viewport(), Qt::LeftButton, Qt::ControlModifier, itemView2->visualRect(index).center());
verifySelectedItemsCount(&view2, 1);
// Paste into the subfolder
view2.pasteIntoFolder();
waitForFinishedPathLoading(&view1);
QCOMPARE(viewItems(&view1), QStringList() << "b");
// The pasted items in view1 are *not* selected now (because the pasting was done indirectly using view2.pasteIntoFolder()).
}
// Private member functions which are used by the tests
/**
* initView(DolphinView*) sets the correct view mode, shows the view on the screen, and waits until loading the
* folder in the view is finished.
*
* Many unit tests need access to DolphinView's internal item view (icons, details, or columns).
* Therefore, a pointer to the item view is returned by initView(DolphinView*).
*/
QAbstractItemView* DolphinViewTest_AllViewModes::initView(DolphinView* view) const
{
QSignalSpy spyFinishedPathLoading(view, SIGNAL(finishedPathLoading(const KUrl&)));
view->setMode(mode());
Q_ASSERT(verifyCorrectViewMode(view));
view->resize(200, 300);
view->show();
QTest::qWaitForWindowShown(view);
// If the DolphinView's finishedPathLoading(const KUrl&) signal has not been received yet,
// we have to wait a bit more.
// The reason why the if-statement is needed here is that the signal might have been emitted
// while we were waiting in QTest::qWaitForWindowShown(view)
// -> waitForFinishedPathLoading(view) would fail in that case.
if (spyFinishedPathLoading.isEmpty()) {
waitForFinishedPathLoading(view);
}
return itemView(view);
}
/**
* verifySelectedItemsCount(int) waits until the DolphinView's selectionChanged(const KFileItemList&)
* signal is received and checks that the selection state of the view is as expected.
*/
void DolphinViewTest_AllViewModes::verifySelectedItemsCount(DolphinView* view, int itemsCount) const
{
QSignalSpy spySelectionChanged(view, SIGNAL(selectionChanged(const KFileItemList&)));
QVERIFY(QTest::kWaitForSignal(view, SIGNAL(selectionChanged(const KFileItemList&)), 2000));
QCOMPARE(view->selectedItems().count(), itemsCount);
QCOMPARE(view->selectedItemsCount(), itemsCount);
QCOMPARE(spySelectionChanged.count(), 1);
QCOMPARE(qvariant_cast<KFileItemList>(spySelectionChanged.at(0).at(0)).count(), itemsCount);
if (itemsCount) {
QVERIFY(view->hasSelection());
}
else {
QVERIFY(!view->hasSelection());
}
}
#include "dolphinviewtest_allviewmodes.moc"

View file

@ -1,80 +0,0 @@
/*****************************************************************************
* Copyright (C) 2010-2011 by Frank Reininghaus (frank78ac@googlemail.com) *
* *
* 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 *
*****************************************************************************/
#ifndef DOLPHINVIEWTEST_ALLVIEWMODES
#define DOLPHINVIEWTEST_ALLVIEWMODES
#include "testbase.h"
#include "views/dolphinview.h"
/**
* DolphinViewTest_AllViewModes is used as a base class for tests that check the
* basic functionality of DolphinView in all view modes. The derived classes
* have to provide implementations for the virtual methods mode() and verifyCorrectViewMode(),
* see below.
*
* Tests for DolphinView functionality that is specific to a particular view mode or
* to switching between different view modes should not be added here, but to another
* DolphinView unit test.
*/
class DolphinViewTest_AllViewModes : public TestBase
{
Q_OBJECT
public:
DolphinViewTest_AllViewModes();
private slots:
void testSelection();
void testViewPropertySettings();
void testZoomLevel();
void testSaveAndRestoreState();
void testKeyboardFocus();
void testCutCopyPaste();
private:
/**
* Sets the correct view mode, shows the view on the screen, and waits until loading the
* folder in the view is finished.
*
* Many unit tests need access to DolphinVie's internal item view (icons, details, or columns).
* Therefore, a pointer to the item view is returned by initView(DolphinView*).
*/
QAbstractItemView* initView(DolphinView* view) const;
/** Returns the view mode (Icons, Details, Columns) to be used in the test. */
virtual DolphinView::Mode mode() const = 0;
/** Should return true if the view mode is correct. */
virtual bool verifyCorrectViewMode(const DolphinView* view) const = 0;
/**
* Waits for the DolphinView's selectionChanged(const KFileItemList&) to be emitted
* and verifies that the number of selected items is as expected.
*/
void verifySelectedItemsCount(DolphinView* view, int itemsCount) const;
};
#endif

View file

@ -0,0 +1,100 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 <qtest_kde.h>
#include <KDirLister>
#include "kitemviews/kfileitemlistview.h"
#include "kitemviews/kfileitemmodel.h"
#include "testdir.h"
#include <QGraphicsView>
namespace {
const int DefaultTimeout = 2000;
};
class KFileItemListViewTest : public QObject
{
Q_OBJECT
private slots:
void init();
void cleanup();
void testFeffi();
private:
KFileItemListView* m_listView;
KFileItemModel* m_model;
KDirLister* m_dirLister;
TestDir* m_testDir;
QGraphicsView* m_graphicsView;
};
void KFileItemListViewTest::init()
{
qRegisterMetaType<KItemRangeList>("KItemRangeList");
qRegisterMetaType<KFileItemList>("KFileItemList");
m_testDir = new TestDir();
m_dirLister = new KDirLister();
m_model = new KFileItemModel(m_dirLister);
m_listView = new KFileItemListView();
m_listView->onModelChanged(m_model, 0);
m_graphicsView = new QGraphicsView();
m_graphicsView->show();
QTest::qWaitForWindowShown(m_graphicsView);
}
void KFileItemListViewTest::cleanup()
{
delete m_graphicsView;
m_graphicsView = 0;
delete m_listView;
m_listView = 0;
delete m_model;
m_model = 0;
delete m_dirLister;
m_dirLister = 0;
delete m_testDir;
m_testDir = 0;
}
void KFileItemListViewTest::testFeffi()
{
QStringList files;
files << "a.txt" << "b.txt" << "c.txt";
m_testDir->createFiles(files);
m_dirLister->openUrl(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 3);
}
QTEST_KDEMAIN(KFileItemListViewTest, GUI)
#include "kfileitemlistviewtest.moc"

View file

@ -0,0 +1,210 @@
/***************************************************************************
* Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 <qtest_kde.h>
#include <KDirLister>
#include "kitemviews/kfileitemmodel.h"
#include "testdir.h"
namespace {
const int DefaultTimeout = 2000;
};
class KFileItemModelTest : public QObject
{
Q_OBJECT
private slots:
void init();
void cleanup();
void testDefaultRoles();
void testDefaultSortRole();
void testDefaultGroupRole();
void testNewItems();
void testInsertingItems();
void testExpansionLevelsCompare_data();
void testExpansionLevelsCompare();
private:
bool isModelConsistent() const;
private:
KFileItemModel* m_model;
KDirLister* m_dirLister;
TestDir* m_testDir;
};
void KFileItemModelTest::init()
{
qRegisterMetaType<KItemRangeList>("KItemRangeList");
qRegisterMetaType<KFileItemList>("KFileItemList");
m_testDir = new TestDir();
m_dirLister = new KDirLister();
m_model = new KFileItemModel(m_dirLister);
}
void KFileItemModelTest::cleanup()
{
delete m_model;
m_model = 0;
delete m_dirLister;
m_dirLister = 0;
delete m_testDir;
m_testDir = 0;
}
void KFileItemModelTest::testDefaultRoles()
{
const QSet<QByteArray> roles = m_model->roles();
QCOMPARE(roles.count(), 2);
QVERIFY(roles.contains("name"));
QVERIFY(roles.contains("isDir"));
}
void KFileItemModelTest::testDefaultSortRole()
{
QCOMPARE(m_model->sortRole(), QByteArray("name"));
QStringList files;
files << "c.txt" << "a.txt" << "b.txt";
m_testDir->createFiles(files);
m_dirLister->openUrl(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 3);
QCOMPARE(m_model->data(0)["name"].toString(), QString("a.txt"));
QCOMPARE(m_model->data(1)["name"].toString(), QString("b.txt"));
QCOMPARE(m_model->data(2)["name"].toString(), QString("c.txt"));
}
void KFileItemModelTest::testDefaultGroupRole()
{
QVERIFY(m_model->groupRole().isEmpty());
}
void KFileItemModelTest::testNewItems()
{
QStringList files;
files << "a.txt" << "b.txt" << "c.txt";
m_testDir->createFiles(files);
m_dirLister->openUrl(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 3);
QVERIFY(isModelConsistent());
}
void KFileItemModelTest::testInsertingItems()
{
// QSKIP("Temporary disabled", SkipSingle);
// KFileItemModel prevents that inserting a punch of items sequentially
// results in an itemsInserted()-signal for each item. Instead internally
// a timeout is given that collects such operations and results in only
// one itemsInserted()-signal. However in this test we want to stress
// KFileItemModel to do a lot of insert operation and hence decrease
// the timeout to 1 millisecond.
m_model->m_minimumUpdateIntervalTimer->setInterval(1);
m_testDir->createFile("1");
m_dirLister->openUrl(m_testDir->url());
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
QCOMPARE(m_model->count(), 1);
// Insert 10 items for 20 times. After each insert operation the model consistency
// is checked.
QSet<int> insertedItems;
for (int i = 0; i < 20; ++i) {
QSignalSpy spy(m_model, SIGNAL(itemsInserted(KItemRangeList)));
for (int j = 0; j < 10; ++j) {
int itemName = qrand();
while (insertedItems.contains(itemName)) {
itemName = qrand();
}
insertedItems.insert(itemName);
m_testDir->createFile(QString::number(itemName));
}
m_dirLister->updateDirectory(m_testDir->url());
if (spy.count() == 0) {
QVERIFY(QTest::kWaitForSignal(m_model, SIGNAL(itemsInserted(KItemRangeList)), DefaultTimeout));
}
QVERIFY(isModelConsistent());
}
QCOMPARE(m_model->count(), 201);
}
void KFileItemModelTest::testExpansionLevelsCompare_data()
{
QTest::addColumn<QString>("urlA");
QTest::addColumn<QString>("urlB");
QTest::addColumn<int>("result");
QTest::newRow("Equal") << "/a/b" << "/a/b" << 0;
QTest::newRow("Sub path: A < B") << "/a/b" << "/a/b/c" << -1;
QTest::newRow("Sub path: A > B") << "/a/b/c" << "/a/b" << +1;
}
void KFileItemModelTest::testExpansionLevelsCompare()
{
QFETCH(QString, urlA);
QFETCH(QString, urlB);
QFETCH(int, result);
const KFileItem a(KUrl(urlA), QString(), mode_t(-1));
const KFileItem b(KUrl(urlB), QString(), mode_t(-1));
QCOMPARE(m_model->expansionLevelsCompare(a, b), result);
}
bool KFileItemModelTest::isModelConsistent() const
{
for (int i = 0; i < m_model->count(); ++i) {
const KFileItem item = m_model->fileItem(i);
if (item.isNull()) {
qWarning() << "Item" << i << "is null";
return false;
}
const int itemIndex = m_model->index(item);
if (itemIndex != i) {
qWarning() << "Item" << i << "has a wrong index:" << itemIndex;
return false;
}
}
return true;
}
QTEST_KDEMAIN(KFileItemModelTest, NoGUI)
#include "kfileitemmodeltest.moc"

View file

@ -1,77 +0,0 @@
/*****************************************************************************
* Copyright (C) 2010-2011 by Frank Reininghaus (frank78ac@googlemail.com) *
* *
* 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 "testbase.h"
#include <qtest_kde.h>
#include "views/dolphinview.h"
#include "views/dolphinmodel.h"
#include "views/dolphindirlister.h"
#include "views/dolphinsortfilterproxymodel.h"
#include <QAbstractItemView>
QAbstractItemView* TestBase::itemView(const DolphinView* view)
{
return view->m_viewAccessor.itemView();
}
void TestBase::waitForFinishedPathLoading(DolphinView* view, int milliseconds)
{
// If the signal is not received, somthing is going seriously wrong.
// -> assert here rather than continuing, which might result in test failures which are hard to unterstand.
bool viewHasFinishedLoading = QTest::kWaitForSignal(view, SIGNAL(finishedPathLoading(const KUrl&)), milliseconds);
Q_ASSERT(viewHasFinishedLoading);
Q_UNUSED(viewHasFinishedLoading) // suppress compiler warining is asserts are disabled
}
void TestBase::reloadViewAndWait(DolphinView* view)
{
view->reload();
waitForFinishedPathLoading(view);
}
QStringList TestBase::viewItems(const DolphinView* view)
{
QStringList itemList;
const QAbstractItemModel* model = itemView(view)->model();
for (int row = 0; row < model->rowCount(); row++) {
itemList << model->data(model->index(row, 0), Qt::DisplayRole).toString();
}
return itemList;
}
QStringList TestBase::selectedItems(const DolphinView* view)
{
QStringList itemList;
const QAbstractItemModel* model = itemView(view)->model();
const QModelIndexList selectedIndexes = itemView(view)->selectionModel()->selectedIndexes();
for (int row = 0; row < model->rowCount(); row++) {
const QModelIndex index = model->index(row, 0);
if (selectedIndexes.contains(index)) {
itemList << model->data(model->index(row, 0), Qt::DisplayRole).toString();
}
}
return itemList;
}

View file

@ -1,67 +0,0 @@
/*****************************************************************************
* Copyright (C) 2010-2011 by Frank Reininghaus (frank78ac@googlemail.com) *
* *
* 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 *
*****************************************************************************/
#ifndef TESTBASE_H
#define TESTBASE_H
#include <QtCore/QObject>
class QAbstractItemView;
class DolphinDirLister;
class DolphinModel;
class DolphinSortFilterProxyModel;
class DolphinView;
/**
* The class TestBase (which is a friend of DolphinView's) provides access to some
* parts of DolphinView to the unit tests.
*
* TODO: TestBase should also backup the DolphinSettings and restore them later!
*/
class TestBase : public QObject
{
Q_OBJECT
public:
TestBase() {};
~TestBase() {};
/** Returns the item view (icons, details, or columns) */
static QAbstractItemView* itemView(const DolphinView* view);
/**
* Waits until the view emits its finishedPathLoading(const KUrl&) signal.
* Asserts if the signal is not received within the given number of milliseconds.
*/
static void waitForFinishedPathLoading(DolphinView* view, int milliseconds=20000);
/** Reloads the view and waits for the finishedPathLoading(const KUrl&) signal. */
static void reloadViewAndWait(DolphinView* view);
/** Returns the items shown in the view. The order corresponds to the sort order of the view. */
static QStringList viewItems(const DolphinView* view);
/** Returns the items which are selected in the view. The order corresponds to the sort order of the view. */
static QStringList selectedItems(const DolphinView* view);
};
#endif

View file

@ -27,8 +27,20 @@
#include <sys/utime.h>
#endif
/** The following function is taken from kdelibs/kio/tests/kiotesthelper.h, copyright (C) 2006 by David Faure */
TestDir::TestDir()
{
}
TestDir::~TestDir()
{
}
KUrl TestDir::url() const
{
return KUrl(name());
}
/** The following function is taken from kdelibs/kio/tests/kiotesthelper.h, copyright (C) 2006 by David Faure */
static void setTimeStamp(const QString& path, const QDateTime& mtime)
{
#ifdef Q_OS_UNIX
@ -63,7 +75,7 @@ void TestDir::createFile(const QString& path, const QByteArray& data, const QDat
void TestDir::createFiles(const QStringList& files)
{
foreach(const QString& path, files) {
foreach (const QString& path, files) {
createFile(path);
}
}

View file

@ -29,29 +29,27 @@
* TestDir provides a temporary directory. In addition to KTempDir, it has
* methods that create files and subdirectories inside the directory.
*/
class TestDir : public KTempDir
{
public:
TestDir();
virtual ~TestDir();
TestDir() {}
~TestDir() {}
KUrl url() const { return KUrl(name()); }
KUrl url() const;
/**
* The following functions create either a file, a list of files, or a directory.
* The paths may be absolute or relative to the test directory. Any missing parent
* directories will be created automatically.
*/
void createFile(const QString& path, const QByteArray& data = QByteArray("test"), const QDateTime& time = QDateTime());
void createFile(const QString& path,
const QByteArray& data = QByteArray("test"),
const QDateTime& time = QDateTime());
void createFiles(const QStringList& files);
void createDir(const QString& path, const QDateTime& time = QDateTime());
private:
void makePathAbsoluteAndCreateParents(QString& path);
};

View file

@ -19,7 +19,6 @@
#include "additionalinfoaccessor.h"
#include "dolphinmodel.h"
#include <KGlobal>
#include <KLocale>
@ -35,31 +34,31 @@ AdditionalInfoAccessor& AdditionalInfoAccessor::instance()
return s_additionalInfoManager->instance;
}
KFileItemDelegate::InformationList AdditionalInfoAccessor::keys() const
QList<DolphinView::AdditionalInfo> AdditionalInfoAccessor::keys() const
{
return m_information;
return m_infoList;
}
KFileItemDelegate::Information AdditionalInfoAccessor::keyForColumn(int columnIndex) const
QByteArray AdditionalInfoAccessor::role(DolphinView::AdditionalInfo info) const
{
KFileItemDelegate::Information info = KFileItemDelegate::NoInformation;
switch (columnIndex) {
case DolphinModel::Size: info = KFileItemDelegate::Size; break;
case DolphinModel::ModifiedTime: info = KFileItemDelegate::ModificationTime; break;
case DolphinModel::Permissions: info = KFileItemDelegate::Permissions; break;
case DolphinModel::Owner: info = KFileItemDelegate::Owner; break;
case DolphinModel::Group: info = KFileItemDelegate::OwnerAndGroup; break;
case DolphinModel::Type: info = KFileItemDelegate::FriendlyMimeType; break;
case DolphinModel::LinkDest: info = KFileItemDelegate::LinkDest; break;
case DolphinModel::LocalPathOrUrl: info = KFileItemDelegate::LocalPathOrUrl; break;
QByteArray role;
switch (info) {
case DolphinView::NameInfo: role = "name"; break;
case DolphinView::SizeInfo: role = "size"; break;
case DolphinView::DateInfo: role = "date"; break;
case DolphinView::PermissionsInfo: role = "permissions"; break;
case DolphinView::OwnerInfo: role = "owner"; break;
case DolphinView::GroupInfo: role = "group"; break;
case DolphinView::TypeInfo: role = "type"; break;
case DolphinView::DestinationInfo: role = "destination"; break;
case DolphinView::PathInfo: role = "path"; break;
default: break;
}
return info;
return role;
}
QString AdditionalInfoAccessor::actionCollectionName(KFileItemDelegate::Information info,
QString AdditionalInfoAccessor::actionCollectionName(DolphinView::AdditionalInfo info,
ActionCollectionType type) const
{
QString name;
@ -76,62 +75,56 @@ QString AdditionalInfoAccessor::actionCollectionName(KFileItemDelegate::Informat
return name;
}
QString AdditionalInfoAccessor::translation(KFileItemDelegate::Information info) const
QString AdditionalInfoAccessor::translation(DolphinView::AdditionalInfo info) const
{
return i18nc(m_map[info]->context, m_map[info]->translation);
}
QString AdditionalInfoAccessor::value(KFileItemDelegate::Information info) const
QString AdditionalInfoAccessor::value(DolphinView::AdditionalInfo info) const
{
return m_map[info]->value;
}
DolphinView::Sorting AdditionalInfoAccessor::sorting(KFileItemDelegate::Information info) const
DolphinView::Sorting AdditionalInfoAccessor::sorting(DolphinView::AdditionalInfo info) const
{
return m_map[info]->sorting;
}
int AdditionalInfoAccessor::bitValue(KFileItemDelegate::Information info) const
{
return m_map[info]->bitValue;
}
AdditionalInfoAccessor::AdditionalInfoAccessor() :
m_information(),
m_infoList(),
m_map()
{
static const AdditionalInfoAccessor::AdditionalInfo additionalInfo[] = {
// Entries for view-properties version 1:
{ "size", I18N_NOOP2_NOSTRIP("@label", "Size"), "Size", DolphinView::SortBySize, 1 },
{ "date", I18N_NOOP2_NOSTRIP("@label", "Date"), "Date", DolphinView::SortByDate, 2 },
{ "permissions", I18N_NOOP2_NOSTRIP("@label", "Permissions"), "Permissions", DolphinView::SortByPermissions, 4 },
{ "owner", I18N_NOOP2_NOSTRIP("@label", "Owner"), "Owner", DolphinView::SortByOwner, 8 },
{ "group", I18N_NOOP2_NOSTRIP("@label", "Group"), "Group", DolphinView::SortByGroup, 16 },
{ "type", I18N_NOOP2_NOSTRIP("@label", "Type"), "Type", DolphinView::SortByType, 32 },
{ "destination", I18N_NOOP2_NOSTRIP("@label", "Link Destination"), "LinkDestination", DolphinView::SortByDestination, 64 },
{ "path", I18N_NOOP2_NOSTRIP("@label", "Path"), "Path", DolphinView::SortByPath, 128 }
// Entries for view-properties version >= 2 (the last column can be set to 0):
{ "size", I18N_NOOP2_NOSTRIP("@label", "Size"), "Size", DolphinView::SortBySize},
{ "date", I18N_NOOP2_NOSTRIP("@label", "Date"), "Date", DolphinView::SortByDate},
{ "permissions", I18N_NOOP2_NOSTRIP("@label", "Permissions"), "Permissions", DolphinView::SortByPermissions},
{ "owner", I18N_NOOP2_NOSTRIP("@label", "Owner"), "Owner", DolphinView::SortByOwner},
{ "group", I18N_NOOP2_NOSTRIP("@label", "Group"), "Group", DolphinView::SortByGroup},
{ "type", I18N_NOOP2_NOSTRIP("@label", "Type"), "Type", DolphinView::SortByType},
{ "destination", I18N_NOOP2_NOSTRIP("@label", "Link Destination"), "LinkDestination", DolphinView::SortByDestination},
{ "path", I18N_NOOP2_NOSTRIP("@label", "Path"), "Path", DolphinView::SortByPath}
};
m_map.insert(KFileItemDelegate::Size, &additionalInfo[0]);
m_map.insert(KFileItemDelegate::ModificationTime, &additionalInfo[1]);
m_map.insert(KFileItemDelegate::Permissions, &additionalInfo[2]);
m_map.insert(KFileItemDelegate::Owner, &additionalInfo[3]);
m_map.insert(KFileItemDelegate::OwnerAndGroup, &additionalInfo[4]);
m_map.insert(KFileItemDelegate::FriendlyMimeType, &additionalInfo[5]);
m_map.insert(KFileItemDelegate::LinkDest, &additionalInfo[6]);
m_map.insert(KFileItemDelegate::LocalPathOrUrl, &additionalInfo[7]);
m_map.insert(DolphinView::SizeInfo, &additionalInfo[0]);
m_map.insert(DolphinView::DateInfo, &additionalInfo[1]);
m_map.insert(DolphinView::PermissionsInfo, &additionalInfo[2]);
m_map.insert(DolphinView::OwnerInfo, &additionalInfo[3]);
m_map.insert(DolphinView::GroupInfo, &additionalInfo[4]);
m_map.insert(DolphinView::TypeInfo, &additionalInfo[5]);
m_map.insert(DolphinView::DestinationInfo, &additionalInfo[6]);
m_map.insert(DolphinView::PathInfo, &additionalInfo[7]);
// The m_information list defines all available keys and the sort order
// (don't use m_information = m_map.keys(), as the order is undefined).
m_information.append(KFileItemDelegate::Size);
m_information.append(KFileItemDelegate::ModificationTime);
m_information.append(KFileItemDelegate::Permissions);
m_information.append(KFileItemDelegate::Owner);
m_information.append(KFileItemDelegate::OwnerAndGroup);
m_information.append(KFileItemDelegate::FriendlyMimeType);
m_information.append(KFileItemDelegate::LinkDest);
m_information.append(KFileItemDelegate::LocalPathOrUrl);
// The m_infoList defines all available keys and the sort order
// (don't use m_information = m_map.keys(), as the order would be undefined).
m_infoList.append(DolphinView::SizeInfo);
m_infoList.append(DolphinView::DateInfo);
m_infoList.append(DolphinView::PermissionsInfo);
m_infoList.append(DolphinView::OwnerInfo);
m_infoList.append(DolphinView::GroupInfo);
m_infoList.append(DolphinView::TypeInfo);
m_infoList.append(DolphinView::DestinationInfo);
m_infoList.append(DolphinView::PathInfo);
}
AdditionalInfoAccessor::~AdditionalInfoAccessor()

View file

@ -60,31 +60,21 @@ public:
* All entries of this list are keys for accessing the corresponding
* data (see actionCollectionName(), translation(), bitValue()).
*/
KFileItemDelegate::InformationList keys() const;
QList<DolphinView::AdditionalInfo> keys() const;
/**
* @return Key for the model column with the index \p columnIndex.
*/
KFileItemDelegate::Information keyForColumn(int columnIndex) const;
QByteArray role(DolphinView::AdditionalInfo info) const;
QString actionCollectionName(KFileItemDelegate::Information info, ActionCollectionType type) const;
QString actionCollectionName(DolphinView::AdditionalInfo info, ActionCollectionType type) const;
QString translation(KFileItemDelegate::Information info) const;
QString translation(DolphinView::AdditionalInfo info) const;
/**
* @return String representation of the value that is stored in the .directory
* by ViewProperties.
*/
QString value(KFileItemDelegate::Information info) const;
QString value(DolphinView::AdditionalInfo info) const;
DolphinView::Sorting sorting(KFileItemDelegate::Information info) const;
/**
* @return Bitvalue for \p info that is stored in a ViewProperties instance.
* Is required only for backward compatibility with the version 1 of
* the view-properties.
*/
int bitValue(KFileItemDelegate::Information info) const;
DolphinView::Sorting sorting(DolphinView::AdditionalInfo info) const;
protected:
AdditionalInfoAccessor();
@ -98,11 +88,10 @@ private:
const char* const translation;
const char* const value;
const DolphinView::Sorting sorting;
const int bitValue; // for backward compatibility with version 1 of view-properties
};
KFileItemDelegate::InformationList m_information;
QMap<KFileItemDelegate::Information, const AdditionalInfo*> m_map;
QList<DolphinView::AdditionalInfo> m_infoList;
QMap<DolphinView::AdditionalInfo, const AdditionalInfo*> m_map;
};
#endif

View file

@ -1,377 +0,0 @@
/*
* This file is part of the KDE project
* Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "dolphincategorydrawer.h"
#include <config-nepomuk.h>
#include <QPainter>
#include <QFile>
#include <QDir>
#include <QApplication>
#include <QStyleOption>
#ifdef HAVE_NEPOMUK
#include <Nepomuk/KRatingPainter>
#endif
#include <KIconLoader>
#include <KIconEffect>
#include <KCategorizedSortFilterProxyModel>
#include <KUser>
#include <KCategorizedView>
#include "dolphinview.h"
#include "dolphinmodel.h"
#define HORIZONTAL_HINT 3
DolphinCategoryDrawer::DolphinCategoryDrawer(KCategorizedView *view)
: KCategoryDrawerV3(view)
, hotSpotPressed(NoneHotSpot)
, selectAll(KIconLoader::global()->loadIcon("list-add", KIconLoader::Desktop, 16))
, selectAllHovered(KIconLoader::global()->iconEffect()->apply(selectAll, KIconLoader::Desktop, KIconLoader::ActiveState))
, selectAllDisabled(KIconLoader::global()->iconEffect()->apply(selectAll, KIconLoader::Desktop, KIconLoader::DisabledState))
, unselectAll(KIconLoader::global()->loadIcon("list-remove", KIconLoader::Desktop, 16))
, unselectAllHovered(KIconLoader::global()->iconEffect()->apply(unselectAll, KIconLoader::Desktop, KIconLoader::ActiveState))
, unselectAllDisabled(KIconLoader::global()->iconEffect()->apply(unselectAll, KIconLoader::Desktop, KIconLoader::DisabledState))
{
}
DolphinCategoryDrawer::~DolphinCategoryDrawer()
{
}
bool DolphinCategoryDrawer::allCategorySelected(const QString &category) const
{
const QModelIndexList list = view()->block(category);
foreach (const QModelIndex &index, list) {
if (!view()->selectionModel()->isSelected(index)) {
return false;
}
}
return true;
}
bool DolphinCategoryDrawer::someCategorySelected(const QString &category) const
{
const QModelIndexList list = view()->block(category);
foreach (const QModelIndex &index, list) {
if (view()->selectionModel()->isSelected(index)) {
return true;
}
}
return false;
}
void DolphinCategoryDrawer::drawCategory(const QModelIndex &index, int sortRole,
const QStyleOption &option, QPainter *painter) const
{
Q_UNUSED(sortRole);
painter->setRenderHint(QPainter::Antialiasing);
if (!index.isValid()) {
return;
}
const QString category = index.model()->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
const QRect optRect = option.rect;
QFont font(QApplication::font());
font.setBold(true);
const QFontMetrics fontMetrics = QFontMetrics(font);
QColor outlineColor = option.palette.text().color();
outlineColor.setAlphaF(0.35);
//BEGIN: top left corner
{
painter->save();
painter->setPen(outlineColor);
const QPointF topLeft(optRect.topLeft());
QRectF arc(topLeft, QSizeF(4, 4));
arc.translate(0.5, 0.5);
painter->drawArc(arc, 1440, 1440);
painter->restore();
}
//END: top left corner
//BEGIN: left vertical line
{
QPoint start(optRect.topLeft());
start.ry() += 3;
QPoint verticalGradBottom(optRect.topLeft());
verticalGradBottom.ry() += fontMetrics.height() + 5;
QLinearGradient gradient(start, verticalGradBottom);
gradient.setColorAt(0, outlineColor);
gradient.setColorAt(1, Qt::transparent);
painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient);
}
//END: left vertical line
//BEGIN: horizontal line
{
QPoint start(optRect.topLeft());
start.rx() += 3;
QPoint horizontalGradTop(optRect.topLeft());
horizontalGradTop.rx() += optRect.width() - 6;
painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor);
}
//END: horizontal line
//BEGIN: top right corner
{
painter->save();
painter->setPen(outlineColor);
QPointF topRight(optRect.topRight());
topRight.rx() -= 4;
QRectF arc(topRight, QSizeF(4, 4));
arc.translate(0.5, 0.5);
painter->drawArc(arc, 0, 1440);
painter->restore();
}
//END: top right corner
//BEGIN: right vertical line
{
QPoint start(optRect.topRight());
start.ry() += 3;
QPoint verticalGradBottom(optRect.topRight());
verticalGradBottom.ry() += fontMetrics.height() + 5;
QLinearGradient gradient(start, verticalGradBottom);
gradient.setColorAt(0, outlineColor);
gradient.setColorAt(1, Qt::transparent);
painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient);
}
//END: right vertical line
const int iconSize = KIconLoader::global()->currentSize(KIconLoader::Small);
//BEGIN: select/unselect all
{
if (this->category == category) {
QRect iconAllRect(option.rect);
iconAllRect.setTop(iconAllRect.top() + 4);
iconAllRect.setLeft(iconAllRect.right() - 16 - 7);
iconAllRect.setSize(QSize(iconSize, iconSize));
if (!allCategorySelected(category)) {
if (iconAllRect.contains(pos)) {
painter->drawPixmap(iconAllRect, selectAllHovered);
} else {
painter->drawPixmap(iconAllRect, selectAll);
}
} else {
painter->drawPixmap(iconAllRect, selectAllDisabled);
}
QRect iconNoneRect(option.rect);
iconNoneRect.setTop(iconNoneRect.top() + 4);
iconNoneRect.setLeft(iconNoneRect.right() - 16 * 2 - 7 * 2);
iconNoneRect.setSize(QSize(iconSize, iconSize));
if (someCategorySelected(category)) {
if (iconNoneRect.contains(pos)) {
painter->drawPixmap(iconNoneRect, unselectAllHovered);
} else {
painter->drawPixmap(iconNoneRect, unselectAll);
}
} else {
painter->drawPixmap(iconNoneRect, unselectAllDisabled);
}
}
}
//END: select/unselect all
//BEGIN: category information
{
bool paintIcon;
QPixmap icon;
switch (index.column()) {
case KDirModel::Owner: {
paintIcon = true;
KUser user(category);
const QString faceIconPath = user.faceIconPath();
if (faceIconPath.isEmpty()) {
icon = KIconLoader::global()->loadIcon("user-identity", KIconLoader::NoGroup, iconSize);
} else {
icon = QPixmap::fromImage(QImage(faceIconPath).scaledToHeight(iconSize, Qt::SmoothTransformation));
}
}
break;
case KDirModel::Type: {
paintIcon = true;
const KCategorizedSortFilterProxyModel *proxyModel = static_cast<const KCategorizedSortFilterProxyModel*>(index.model());
const DolphinModel *model = static_cast<const DolphinModel*>(proxyModel->sourceModel());
KFileItem item = model->itemForIndex(proxyModel->mapToSource(index));
// This is the only way of getting the icon right. Others will fail on corner
// cases like the item representing this group has been set a different icon,
// so the group icon drawn is that one particularly. This way assures the drawn
// icon is the one of the mimetype of the group itself. (ereslibre)
icon = KIconLoader::global()->loadMimeTypeIcon(item.mimeTypePtr()->iconName(), KIconLoader::NoGroup, iconSize);
}
break;
default:
paintIcon = false;
}
if (paintIcon) {
QRect iconRect(option.rect);
iconRect.setTop(iconRect.top() + 4);
iconRect.setLeft(iconRect.left() + 7);
iconRect.setSize(QSize(iconSize, iconSize));
painter->drawPixmap(iconRect, icon);
}
//BEGIN: text
{
QRect textRect(option.rect);
textRect.setTop(textRect.top() + 7);
textRect.setLeft(textRect.left() + 7 + (paintIcon ? (iconSize + 6) : 0));
textRect.setHeight(qMax(fontMetrics.height(), iconSize));
textRect.setRight(textRect.right() - 7);
textRect.setBottom(textRect.bottom() - 5); // only one pixel separation here (no gradient)
painter->save();
painter->setFont(font);
QColor penColor(option.palette.text().color());
penColor.setAlphaF(0.6);
painter->setPen(penColor);
painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, category);
painter->restore();
}
//END: text
}
//BEGIN: category information
}
int DolphinCategoryDrawer::categoryHeight(const QModelIndex &index, const QStyleOption &) const
{
int iconSize = KIconLoader::global()->currentSize(KIconLoader::Small);
QFont font(QApplication::font());
font.setBold(true);
const QFontMetrics fontMetrics = QFontMetrics(font);
int heightWithoutIcon = fontMetrics.height() + (iconSize / 4) * 2 + 1; /* 1 pixel-width gradient */
bool paintIcon;
switch (index.column()) {
case KDirModel::Owner:
case KDirModel::Type:
paintIcon = true;
break;
default:
paintIcon = false;
}
if (paintIcon) {
return qMax(heightWithoutIcon + 5, iconSize + 1 /* 1 pixel-width gradient */
+ 5 /* top and bottom separation */);
}
return heightWithoutIcon + 5;
}
void DolphinCategoryDrawer::mouseButtonPressed(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event)
{
if (!index.isValid()) {
event->ignore();
return;
}
const QString category = index.model()->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
int iconSize = KIconLoader::global()->currentSize(KIconLoader::Small);
if (this->category == category) {
QRect iconAllRect(blockRect);
iconAllRect.setTop(iconAllRect.top() + 4);
iconAllRect.setLeft(iconAllRect.right() - 16 - 7);
iconAllRect.setSize(QSize(iconSize, iconSize));
if (iconAllRect.contains(pos)) {
event->accept();
hotSpotPressed = SelectAllHotSpot;
categoryPressed = index;
return;
}
QRect iconNoneRect(blockRect);
iconNoneRect.setTop(iconNoneRect.top() + 4);
iconNoneRect.setLeft(iconNoneRect.right() - 16 * 2 - 7 * 2);
iconNoneRect.setSize(QSize(iconSize, iconSize));
if (iconNoneRect.contains(pos)) {
event->accept();
hotSpotPressed = UnselectAllHotSpot;
categoryPressed = index;
return;
}
}
event->ignore();
}
void DolphinCategoryDrawer::mouseButtonReleased(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event)
{
if (!index.isValid() || hotSpotPressed == NoneHotSpot || categoryPressed != index) {
event->ignore();
return;
}
categoryPressed = QModelIndex();
const QString category = index.model()->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
int iconSize = KIconLoader::global()->currentSize(KIconLoader::Small);
if (this->category == category) {
QRect iconAllRect(blockRect);
iconAllRect.setTop(iconAllRect.top() + 4);
iconAllRect.setLeft(iconAllRect.right() - 16 - 7);
iconAllRect.setSize(QSize(iconSize, iconSize));
if (iconAllRect.contains(pos)) {
if (hotSpotPressed == SelectAllHotSpot) {
event->accept();
emit actionRequested(SelectAll, index);
} else {
event->ignore();
hotSpotPressed = NoneHotSpot;
}
return;
}
QRect iconNoneRect(blockRect);
iconNoneRect.setTop(iconNoneRect.top() + 4);
iconNoneRect.setLeft(iconNoneRect.right() - 16 * 2 - 7 * 2);
iconNoneRect.setSize(QSize(iconSize, iconSize));
if (iconNoneRect.contains(pos)) {
if (hotSpotPressed == UnselectAllHotSpot) {
event->accept();
emit actionRequested(UnselectAll, index);
} else {
event->ignore();
hotSpotPressed = NoneHotSpot;
}
return;
}
}
event->ignore();
}
void DolphinCategoryDrawer::mouseMoved(const QModelIndex &index, const QRect &, QMouseEvent *event)
{
event->ignore();
if (!index.isValid()) {
return;
}
pos = event->pos();
category = index.model()->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString();
}
void DolphinCategoryDrawer::mouseLeft(const QModelIndex &, const QRect &)
{
pos = QPoint();
category.clear();
}

View file

@ -1,85 +0,0 @@
/* This file is part of the KDE project
* Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef DOLPHINCATEGORYDRAWER_H
#define DOLPHINCATEGORYDRAWER_H
#include <kcategorydrawer.h>
#include <QStyleOption>
#include <QModelIndex>
#include <libdolphin_export.h>
class LIBDOLPHINPRIVATE_EXPORT DolphinCategoryDrawer
: public KCategoryDrawerV3
{
public:
using KCategoryDrawerV2::mouseButtonPressed;
using KCategoryDrawerV2::mouseButtonReleased;
enum Action {
SelectAll = 0,
UnselectAll
};
DolphinCategoryDrawer(KCategorizedView *view);
virtual ~DolphinCategoryDrawer();
bool allCategorySelected(const QString &category) const;
bool someCategorySelected(const QString &category) const;
virtual void drawCategory(const QModelIndex &index, int sortRole,
const QStyleOption &option, QPainter *painter) const;
virtual int categoryHeight(const QModelIndex &index, const QStyleOption &option) const;
protected:
virtual void mouseButtonPressed(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event);
virtual void mouseButtonReleased(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event);
virtual void mouseMoved(const QModelIndex &index, const QRect &blockRect, QMouseEvent *event);
virtual void mouseLeft(const QModelIndex &index,const QRect &blockRect);
private:
enum HotSpot {
NoneHotSpot = 0,
SelectAllHotSpot,
UnselectAllHotSpot
};
HotSpot hotSpotPressed;
QModelIndex categoryPressed;
QPixmap selectAll;
QPixmap selectAllHovered;
QPixmap selectAllDisabled;
QPixmap unselectAll;
QPixmap unselectAllHovered;
QPixmap unselectAllDisabled;
QPoint pos;
QString category;
};
#endif // DOLPHINCATEGORYDRAWER_H

View file

@ -1,621 +0,0 @@
/***************************************************************************
* Copyright (C) 2007-2009 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 "dolphincolumnview.h"
#include "dolphinmodel.h"
#include "dolphincolumnviewcontainer.h"
#include "dolphinviewcontroller.h"
#include "dolphindirlister.h"
#include "dolphinfileitemdelegate.h"
#include "dolphinsortfilterproxymodel.h"
#include "settings/dolphinsettings.h"
#include "dolphinviewautoscroller.h"
#include "dolphin_columnmodesettings.h"
#include "dolphin_generalsettings.h"
#include "draganddrophelper.h"
#include "folderexpander.h"
#include "tooltips/tooltipmanager.h"
#include "viewextensionsfactory.h"
#include "viewmodecontroller.h"
#include "zoomlevelinfo.h"
#include <KColorScheme>
#include <KDirLister>
#include <KFileItem>
#include <KIO/PreviewJob>
#include <KIcon>
#include <KIconEffect>
#include <KJob>
#include <KLocale>
#include <konqmimedata.h>
#include <QApplication>
#include <QClipboard>
#include <QHeaderView>
#include <QLabel>
#include <QPainter>
#include <QPoint>
#include <QScrollBar>
DolphinColumnView::DolphinColumnView(QWidget* parent,
DolphinColumnViewContainer* container,
const KUrl& url) :
DolphinTreeView(parent),
m_active(false),
m_container(container),
m_extensionsFactory(0),
m_url(url),
m_childUrl(),
m_font(),
m_decorationSize(),
m_dirLister(0),
m_dolphinModel(0),
m_proxyModel(0),
m_resizeWidget(0),
m_resizeXOrigin(-1)
{
setMouseTracking(true);
setAcceptDrops(true);
setUniformRowHeights(true);
setSelectionBehavior(SelectItems);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setDragDropMode(QAbstractItemView::DragDrop);
setDropIndicatorShown(false);
setRootIsDecorated(false);
setItemsExpandable(false);
setEditTriggers(QAbstractItemView::NoEditTriggers);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
setVerticalScrollMode(QTreeView::ScrollPerPixel);
m_resizeWidget = new QLabel(this);
m_resizeWidget->setPixmap(KIcon("transform-move").pixmap(KIconLoader::SizeSmall));
m_resizeWidget->setToolTip(i18nc("@info:tooltip", "Resize column"));
setCornerWidget(m_resizeWidget);
m_resizeWidget->installEventFilter(this);
const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
Q_ASSERT(settings);
if (settings->useSystemFont()) {
m_font = KGlobalSettings::generalFont();
} else {
m_font = QFont(settings->fontFamily(),
qRound(settings->fontSize()),
settings->fontWeight(),
settings->italicFont());
m_font.setPointSizeF(settings->fontSize());
}
setMinimumWidth(settings->fontSize() * 10);
setMaximumWidth(settings->columnWidth());
connect(this, SIGNAL(viewportEntered()),
m_container->m_dolphinViewController, SLOT(emitViewportEntered()));
connect(this, SIGNAL(entered(const QModelIndex&)),
this, SLOT(slotEntered(const QModelIndex&)));
const DolphinView* dolphinView = m_container->m_dolphinViewController->view();
connect(dolphinView, SIGNAL(showPreviewChanged()),
this, SLOT(slotShowPreviewChanged()));
m_dirLister = new DolphinDirLister();
m_dirLister->setAutoUpdate(true);
m_dirLister->setMainWindow(window());
m_dirLister->setDelayedMimeTypes(true);
const bool showHiddenFiles = m_container->m_dolphinViewController->view()->showHiddenFiles();
m_dirLister->setShowingDotFiles(showHiddenFiles);
connect(m_dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted()));
m_dolphinModel = new DolphinModel(this);
m_dolphinModel->setDirLister(m_dirLister);
m_dolphinModel->setDropsAllowed(DolphinModel::DropOnDirectory);
m_proxyModel = new DolphinSortFilterProxyModel(this);
m_proxyModel->setSourceModel(m_dolphinModel);
m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
m_proxyModel->setSorting(dolphinView->sorting());
m_proxyModel->setSortOrder(dolphinView->sortOrder());
m_proxyModel->setSortFoldersFirst(dolphinView->sortFoldersFirst());
setModel(m_proxyModel);
connect(KGlobalSettings::self(), SIGNAL(kdisplayFontChanged()),
this, SLOT(updateFont()));
const ViewModeController* viewModeController = m_container->m_viewModeController;
connect(viewModeController, SIGNAL(zoomLevelChanged(int)),
this, SLOT(setZoomLevel(int)));
const QString nameFilter = viewModeController->nameFilter();
if (!nameFilter.isEmpty()) {
m_proxyModel->setFilterFixedString(nameFilter);
}
updateDecorationSize(dolphinView->showPreview());
updateBackground();
DolphinViewController* dolphinViewController = m_container->m_dolphinViewController;
m_extensionsFactory = new ViewExtensionsFactory(this, dolphinViewController, viewModeController);
m_extensionsFactory->fileItemDelegate()->setMinimizedNameColumn(true);
m_dirLister->openUrl(url, KDirLister::NoFlags);
}
DolphinColumnView::~DolphinColumnView()
{
delete m_proxyModel;
m_proxyModel = 0;
delete m_dolphinModel;
m_dolphinModel = 0;
m_dirLister = 0; // deleted by m_dolphinModel
}
void DolphinColumnView::setActive(bool active)
{
if (m_active != active) {
m_active = active;
if (active) {
activate();
} else {
deactivate();
}
}
}
bool DolphinColumnView::isActive() const
{
return m_active;
}
void DolphinColumnView::setChildUrl(const KUrl& url)
{
m_childUrl = url;
}
KUrl DolphinColumnView::childUrl() const
{
return m_childUrl;
}
void DolphinColumnView::setUrl(const KUrl& url)
{
if (url != m_url) {
m_url = url;
m_dirLister->openUrl(url, KDirLister::NoFlags);
}
}
KUrl DolphinColumnView::url() const
{
return m_url;
}
void DolphinColumnView::updateBackground()
{
// TODO: The alpha-value 150 is copied from DolphinView::setActive(). When
// cleaning up the cut-indication of DolphinColumnView with the code from
// DolphinView a common helper-class should be available which can be shared
// by all view implementations -> no hardcoded value anymore
const QPalette::ColorRole role = viewport()->backgroundRole();
QColor color = viewport()->palette().color(role);
color.setAlpha((m_active && m_container->m_active) ? 255 : 150);
QPalette palette = viewport()->palette();
palette.setColor(role, color);
viewport()->setPalette(palette);
update();
}
KFileItem DolphinColumnView::itemAt(const QPoint& pos) const
{
KFileItem item;
const QModelIndex index = indexAt(pos);
if (index.isValid() && (index.column() == DolphinModel::Name)) {
const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
item = m_dolphinModel->itemForIndex(dolphinModelIndex);
}
return item;
}
void DolphinColumnView::setSelectionModel(QItemSelectionModel* model)
{
// If a change of the selection is done although the view is not active
// (e. g. by the selection markers), the column must be activated. This
// is done by listening to the current selectionChanged() signal.
if (selectionModel()) {
disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
this, SLOT(requestActivation()));
}
DolphinTreeView::setSelectionModel(model);
connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
this, SLOT(requestActivation()));
}
QStyleOptionViewItem DolphinColumnView::viewOptions() const
{
QStyleOptionViewItem viewOptions = DolphinTreeView::viewOptions();
viewOptions.font = m_font;
viewOptions.fontMetrics = QFontMetrics(m_font);
viewOptions.decorationSize = m_decorationSize;
viewOptions.showDecorationSelected = true;
return viewOptions;
}
bool DolphinColumnView::event(QEvent* event)
{
if (event->type() == QEvent::Polish) {
// Hide all columns except of the 'Name' column
for (int i = DolphinModel::Name + 1; i < DolphinModel::ExtraColumnCount; ++i) {
hideColumn(i);
}
header()->hide();
}
return DolphinTreeView::event(event);
}
void DolphinColumnView::startDrag(Qt::DropActions supportedActions)
{
DragAndDropHelper::instance().startDrag(this, supportedActions, m_container->m_dolphinViewController);
DolphinTreeView::startDrag(supportedActions);
}
void DolphinColumnView::dragEnterEvent(QDragEnterEvent* event)
{
event->acceptProposedAction();
requestActivation();
DolphinTreeView::dragEnterEvent(event);
}
void DolphinColumnView::dragMoveEvent(QDragMoveEvent* event)
{
DolphinTreeView::dragMoveEvent(event);
event->acceptProposedAction();
}
void DolphinColumnView::dropEvent(QDropEvent* event)
{
const QModelIndex index = indexAt(event->pos());
m_container->m_dolphinViewController->setItemView(this);
const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
const KFileItem item = m_dolphinModel->itemForIndex(dolphinModelIndex);
m_container->m_dolphinViewController->indicateDroppedUrls(item, event);
DolphinTreeView::dropEvent(event);
}
void DolphinColumnView::paintEvent(QPaintEvent* event)
{
if (!m_childUrl.isEmpty()) {
// Indicate the shown URL of the next column by highlighting the shown folder item
const QModelIndex dirIndex = m_dolphinModel->indexForUrl(m_childUrl);
const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
if (proxyIndex.isValid() && !selectionModel()->isSelected(proxyIndex)) {
QPainter painter(viewport());
QStyleOptionViewItemV4 option;
option.initFrom(this);
option.rect = visualRect(proxyIndex);
option.state = QStyle::State_Enabled | QStyle::State_HasFocus;
option.viewItemPosition = QStyleOptionViewItemV4::OnlyOne;
style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this);
}
}
DolphinTreeView::paintEvent(event);
}
void DolphinColumnView::mousePressEvent(QMouseEvent* event)
{
requestActivation();
if (!indexAt(event->pos()).isValid() && (QApplication::mouseButtons() & Qt::MidButton)) {
m_container->m_dolphinViewController->replaceUrlByClipboard();
}
DolphinTreeView::mousePressEvent(event);
}
void DolphinColumnView::keyPressEvent(QKeyEvent* event)
{
const bool hadSelection = selectionModel()->hasSelection();
DolphinTreeView::keyPressEvent(event);
DolphinViewController* controller = m_container->m_dolphinViewController;
controller->handleKeyPressEvent(event);
switch (event->key()) {
case Qt::Key_Right: {
// Special key handling for the column: A Key_Right should
// open a new column for the currently selected folder.
QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(currentIndex());
// If there is no selection we automatically move to the child url
// instead of the first directory.
// See BUG:263110
if (!hadSelection && !childUrl().isEmpty()) {
dolphinModelIndex = m_dolphinModel->indexForUrl(childUrl());
}
const KFileItem item = m_dolphinModel->itemForIndex(dolphinModelIndex);
if (!item.isNull() && item.isDir()) {
controller->emitItemTriggered(item);
}
break;
}
case Qt::Key_Escape:
selectionModel()->setCurrentIndex(selectionModel()->currentIndex(),
QItemSelectionModel::Current |
QItemSelectionModel::Clear);
break;
default:
break;
}
}
void DolphinColumnView::contextMenuEvent(QContextMenuEvent* event)
{
requestActivation();
DolphinTreeView::contextMenuEvent(event);
m_container->m_dolphinViewController->triggerContextMenuRequest(event->pos());
}
void DolphinColumnView::wheelEvent(QWheelEvent* event)
{
const int step = m_decorationSize.height();
verticalScrollBar()->setSingleStep(step);
DolphinTreeView::wheelEvent(event);
}
void DolphinColumnView::leaveEvent(QEvent* event)
{
DolphinTreeView::leaveEvent(event);
// if the mouse is above an item and moved very fast outside the widget,
// no viewportEntered() signal might be emitted although the mouse has been moved
// above the viewport
m_container->m_dolphinViewController->emitViewportEntered();
}
void DolphinColumnView::currentChanged(const QModelIndex& current, const QModelIndex& previous)
{
DolphinTreeView::currentChanged(current, previous);
m_extensionsFactory->handleCurrentIndexChange(current, previous);
}
QRect DolphinColumnView::visualRect(const QModelIndex& index) const
{
QRect rect = DolphinTreeView::visualRect(index);
const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
const KFileItem item = m_dolphinModel->itemForIndex(dolphinModelIndex);
if (!item.isNull()) {
const int width = DolphinFileItemDelegate::nameColumnWidth(item.text(), viewOptions());
rect.setWidth(width);
}
return rect;
}
bool DolphinColumnView::acceptsDrop(const QModelIndex& index) const
{
if (index.isValid() && (index.column() == DolphinModel::Name)) {
// Accept drops above directories
const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
const KFileItem item = m_dolphinModel->itemForIndex(dolphinModelIndex);
return !item.isNull() && item.isDir();
}
return false;
}
bool DolphinColumnView::eventFilter(QObject* watched, QEvent* event)
{
if (watched == m_resizeWidget) {
switch (event->type()) {
case QEvent::MouseButtonPress: {
// Initiate the resizing of the column
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
m_resizeXOrigin = mouseEvent->globalX();
m_resizeWidget->setMouseTracking(true);
event->accept();
return true;
}
case QEvent::MouseButtonDblClick: {
// Reset the column width to the default value
const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
setMaximumWidth(settings->columnWidth());
m_container->layoutColumns();
m_resizeWidget->setMouseTracking(false);
m_resizeXOrigin = -1;
event->accept();
return true;
}
case QEvent::MouseMove: {
// Resize the column and trigger a relayout of the container
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
int requestedWidth = maximumWidth() - m_resizeXOrigin + mouseEvent->globalX();;
if (requestedWidth < minimumWidth()) {
requestedWidth = minimumWidth();
}
setMaximumWidth(requestedWidth);
m_container->layoutColumns();
m_resizeXOrigin = mouseEvent->globalX();
event->accept();
return true;
}
case QEvent::MouseButtonRelease: {
// The resizing has been finished
m_resizeWidget->setMouseTracking(false);
m_resizeXOrigin = -1;
event->accept();
return true;
}
default:
break;
}
}
return DolphinTreeView::eventFilter(watched, event);
}
void DolphinColumnView::setZoomLevel(int level)
{
const int size = ZoomLevelInfo::iconSizeForZoomLevel(level);
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
const bool showPreview = m_container->m_dolphinViewController->view()->showPreview();
if (showPreview) {
settings->setPreviewSize(size);
} else {
settings->setIconSize(size);
}
updateDecorationSize(showPreview);
}
void DolphinColumnView::slotEntered(const QModelIndex& index)
{
m_container->m_dolphinViewController->setItemView(this);
m_container->m_dolphinViewController->emitItemEntered(index);
}
void DolphinColumnView::requestActivation()
{
m_container->m_dolphinViewController->requestActivation();
if (!m_active) {
m_container->requestActivation(this);
selectionModel()->clear();
}
}
void DolphinColumnView::updateFont()
{
const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
Q_ASSERT(settings);
if (settings->useSystemFont()) {
m_font = KGlobalSettings::generalFont();
}
}
void DolphinColumnView::slotShowPreviewChanged()
{
const DolphinView* view = m_container->m_dolphinViewController->view();
updateDecorationSize(view->showPreview());
}
void DolphinColumnView::slotDirListerCompleted()
{
if (!m_childUrl.isEmpty()) {
return;
}
// Try to optimize the width of the column, so that no name gets clipped
const int requiredWidth = sizeHintForColumn(DolphinModel::Name);
const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
if (requiredWidth > settings->columnWidth()) {
int frameAroundContents = 0;
if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) {
// TODO: Using 2 PM_DefaultFrameWidths are not sufficient. Check Qt-code
// for other pixelmetrics that should be added...
frameAroundContents = style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 4;
}
const int scrollBarWidth = style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, verticalScrollBar());
setMaximumWidth(requiredWidth + frameAroundContents + scrollBarWidth);
m_container->layoutColumns();
if (m_active) {
m_container->assureVisibleActiveColumn();
}
}
}
void DolphinColumnView::activate()
{
setFocus(Qt::OtherFocusReason);
connect(this, SIGNAL(clicked(const QModelIndex&)),
m_container->m_dolphinViewController, SLOT(requestTab(const QModelIndex&)));
if (KGlobalSettings::singleClick()) {
connect(this, SIGNAL(clicked(const QModelIndex&)),
m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
} else {
connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
}
if (selectionModel() && selectionModel()->currentIndex().isValid()) {
selectionModel()->setCurrentIndex(selectionModel()->currentIndex(), QItemSelectionModel::SelectCurrent);
}
updateBackground();
}
void DolphinColumnView::deactivate()
{
clearFocus();
disconnect(this, SIGNAL(clicked(const QModelIndex&)),
m_container->m_dolphinViewController, SLOT(requestTab(const QModelIndex&)));
if (KGlobalSettings::singleClick()) {
disconnect(this, SIGNAL(clicked(const QModelIndex&)),
m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
} else {
disconnect(this, SIGNAL(doubleClicked(const QModelIndex&)),
m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
}
// It is important to disconnect the connection to requestActivation() temporary, otherwise the internal
// clearing of the selection would result in activating the column again.
disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
this, SLOT(requestActivation()));
const QModelIndex current = selectionModel()->currentIndex();
selectionModel()->clear();
selectionModel()->setCurrentIndex(current, QItemSelectionModel::NoUpdate);
connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
this, SLOT(requestActivation()));
updateBackground();
}
void DolphinColumnView::updateDecorationSize(bool showPreview)
{
ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
const int iconSize = showPreview ? settings->previewSize() : settings->iconSize();
const QSize size(iconSize, iconSize);
setIconSize(size);
m_decorationSize = size;
doItemsLayout();
}
#include "dolphincolumnview.moc"

View file

@ -1,151 +0,0 @@
/***************************************************************************
* Copyright (C) 2007-2009 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
***************************************************************************/
#ifndef DOLPHINCOLUMNVIEW_H
#define DOLPHINCOLUMNVIEW_H
#include "dolphinview.h"
#include "dolphintreeview.h"
#include <QFont>
#include <QSize>
#include <QStyleOption>
#include <KUrl>
class DolphinColumnViewContainer;
class DolphinModel;
class DolphinSortFilterProxyModel;
class DolphinDirLister;
class KFileItem;
class QLabel;
class SelectionManager;
class ViewExtensionsFactory;
/**
* Represents one column inside the DolphinColumnViewContainer.
*/
class DolphinColumnView : public DolphinTreeView
{
Q_OBJECT
public:
DolphinColumnView(QWidget* parent,
DolphinColumnViewContainer* container,
const KUrl& url);
virtual ~DolphinColumnView();
/**
* An active column is defined as column, which shows the same URL
* as indicated by the URL navigator. The active column is usually
* drawn in a lighter color. All operations are applied to this column.
*/
void setActive(bool active);
bool isActive() const;
/**
* Sets the directory URL of the child column that is shown next to
* this column. This property is used for a visual indication
* of the shown directory, it does not trigger a loading of the model.
* When no url is selected and the user presses right, then child
* url will be used as column.
*/
void setChildUrl(const KUrl& url);
KUrl childUrl() const;
/** Sets the directory URL that is shown inside the column widget. */
void setUrl(const KUrl& url);
/** Returns the directory URL that is shown inside the column widget. */
KUrl url() const;
/**
* Updates the background color dependent from the activation state
* \a isViewActive of the column view.
*/
void updateBackground();
/**
* Returns the item on the position \a pos. The KFileItem instance
* is null if no item is below the position.
*/
KFileItem itemAt(const QPoint& pos) const;
virtual void setSelectionModel(QItemSelectionModel* model);
protected:
virtual QStyleOptionViewItem viewOptions() const;
virtual bool event(QEvent* event);
virtual void startDrag(Qt::DropActions supportedActions);
virtual void dragEnterEvent(QDragEnterEvent* event);
virtual void dragMoveEvent(QDragMoveEvent* event);
virtual void dropEvent(QDropEvent* event);
virtual void paintEvent(QPaintEvent* event);
virtual void mousePressEvent(QMouseEvent* event);
virtual void keyPressEvent(QKeyEvent* event);
virtual void contextMenuEvent(QContextMenuEvent* event);
virtual void wheelEvent(QWheelEvent* event);
virtual void leaveEvent(QEvent* event);
virtual void currentChanged(const QModelIndex& current, const QModelIndex& previous);
virtual QRect visualRect(const QModelIndex& index) const;
virtual bool acceptsDrop(const QModelIndex& index) const;
virtual bool eventFilter(QObject* watched, QEvent* event);
private slots:
void setZoomLevel(int level);
void slotEntered(const QModelIndex& index);
void requestActivation();
void updateFont();
void slotShowPreviewChanged();
void slotDirListerCompleted();
private:
/** Used by DolphinColumnView::setActive(). */
void activate();
/** Used by DolphinColumnView::setActive(). */
void deactivate();
void updateDecorationSize(bool showPreview);
private:
bool m_active;
DolphinColumnViewContainer* m_container;
SelectionManager* m_selectionManager;
ViewExtensionsFactory* m_extensionsFactory;
KUrl m_url; // URL of the directory that is shown
KUrl m_childUrl; // URL of the next column that is shown
QFont m_font;
QSize m_decorationSize;
DolphinDirLister* m_dirLister;
DolphinModel* m_dolphinModel;
DolphinSortFilterProxyModel* m_proxyModel;
QLabel* m_resizeWidget;
int m_resizeXOrigin;
friend class DolphinColumnViewContainer;
};
#endif

View file

@ -1,427 +0,0 @@
/***************************************************************************
* Copyright (C) 2007-2009 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 "dolphincolumnviewcontainer.h"
#include "dolphin_columnmodesettings.h"
#include "dolphincolumnview.h"
#include "dolphinviewcontroller.h"
#include "dolphinsortfilterproxymodel.h"
#include "draganddrophelper.h"
#include "settings/dolphinsettings.h"
#include "viewmodecontroller.h"
#include <QPoint>
#include <QScrollBar>
#include <QTimeLine>
#include <QTimer>
DolphinColumnViewContainer::DolphinColumnViewContainer(QWidget* parent,
DolphinViewController* dolphinViewController,
const ViewModeController* viewModeController) :
QScrollArea(parent),
m_dolphinViewController(dolphinViewController),
m_viewModeController(viewModeController),
m_active(false),
m_index(-1),
m_contentX(0),
m_columns(),
m_emptyViewport(0),
m_animation(0),
m_dragSource(0),
m_activeUrlTimer(0),
m_assureVisibleActiveColumnTimer(0)
{
Q_ASSERT(dolphinViewController);
Q_ASSERT(viewModeController);
setAcceptDrops(true);
setFocusPolicy(Qt::NoFocus);
setFrameShape(QFrame::NoFrame);
setLayoutDirection(Qt::LeftToRight);
connect(viewModeController, SIGNAL(activationChanged(bool)),
this, SLOT(updateColumnsBackground(bool)));
connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
this, SLOT(moveContentHorizontally(int)));
m_animation = new QTimeLine(500, this);
connect(m_animation, SIGNAL(frameChanged(int)), horizontalScrollBar(), SLOT(setValue(int)));
m_activeUrlTimer = new QTimer(this);
m_activeUrlTimer->setSingleShot(true);
m_activeUrlTimer->setInterval(200);
connect(m_activeUrlTimer, SIGNAL(timeout()),
this, SLOT(updateActiveUrl()));
// Assuring that the active column gets fully visible is done with a small delay. This
// prevents that for temporary activations an animation gets started (e. g. when clicking
// on any folder of the parent column, the child column gets activated).
m_assureVisibleActiveColumnTimer = new QTimer(this);
m_assureVisibleActiveColumnTimer->setSingleShot(true);
m_assureVisibleActiveColumnTimer->setInterval(200);
connect(m_assureVisibleActiveColumnTimer, SIGNAL(timeout()),
this, SLOT(slotAssureVisibleActiveColumn()));
DolphinColumnView* column = new DolphinColumnView(viewport(), this, viewModeController->url());
m_columns.append(column);
requestActivation(column);
m_emptyViewport = new QFrame(viewport());
m_emptyViewport->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
updateColumnsBackground(true);
}
DolphinColumnViewContainer::~DolphinColumnViewContainer()
{
delete m_dragSource;
m_dragSource = 0;
}
KUrl DolphinColumnViewContainer::rootUrl() const
{
return m_columns[0]->url();
}
QAbstractItemView* DolphinColumnViewContainer::activeColumn() const
{
return m_columns[m_index];
}
void DolphinColumnViewContainer::showColumn(const KUrl& url)
{
if (!rootUrl().isParentOf(url)) {
removeAllColumns();
m_columns[0]->setUrl(url);
return;
}
int columnIndex = 0;
foreach (DolphinColumnView* column, m_columns) {
if (column->url().equals(url, KUrl::CompareWithoutTrailingSlash)) {
// the column represents already the requested URL, hence activate it
requestActivation(column);
layoutColumns();
return;
} else if (!column->url().isParentOf(url)) {
// the column is no parent of the requested URL, hence
// just delete all remaining columns
if (columnIndex > 0) {
QList<DolphinColumnView*>::iterator start = m_columns.begin() + columnIndex;
QList<DolphinColumnView*>::iterator end = m_columns.end();
for (QList<DolphinColumnView*>::iterator it = start; it != end; ++it) {
deleteColumn(*it);
}
m_columns.erase(start, end);
const int maxIndex = m_columns.count() - 1;
Q_ASSERT(maxIndex >= 0);
if (m_index > maxIndex) {
m_index = maxIndex;
}
break;
}
}
++columnIndex;
}
// Create missing columns. Assuming that the path is "/home/peter/Temp/" and
// the target path is "/home/peter/Temp/a/b/c/", then the columns "a", "b" and
// "c" will be created.
const int lastIndex = m_columns.count() - 1;
Q_ASSERT(lastIndex >= 0);
const KUrl& activeUrl = m_columns[lastIndex]->url();
Q_ASSERT(activeUrl.isParentOf(url));
Q_ASSERT(activeUrl != url);
QString path = activeUrl.url(KUrl::AddTrailingSlash);
const QString targetPath = url.url(KUrl::AddTrailingSlash);
columnIndex = lastIndex;
int slashIndex = path.count('/');
bool hasSubPath = (slashIndex >= 0);
while (hasSubPath) {
const QString subPath = targetPath.section('/', slashIndex, slashIndex);
if (subPath.isEmpty()) {
hasSubPath = false;
} else {
path += subPath + '/';
++slashIndex;
const KUrl childUrl = KUrl(path);
m_columns[columnIndex]->setChildUrl(childUrl);
columnIndex++;
DolphinColumnView* column = new DolphinColumnView(viewport(), this, childUrl);
m_columns.append(column);
// Before invoking layoutColumns() the column must be set visible temporary.
// To prevent a flickering the initial geometry is set to a hidden position.
column->setGeometry(QRect(-1, -1, 1, 1));
column->show();
layoutColumns();
}
}
requestActivation(m_columns[columnIndex]);
}
void DolphinColumnViewContainer::mousePressEvent(QMouseEvent* event)
{
m_dolphinViewController->requestActivation();
QScrollArea::mousePressEvent(event);
}
void DolphinColumnViewContainer::keyPressEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Left) {
if (m_index > 0) {
requestActivation(m_columns[m_index - 1]);
}
} else {
QScrollArea::keyPressEvent(event);
}
}
void DolphinColumnViewContainer::resizeEvent(QResizeEvent* event)
{
QScrollArea::resizeEvent(event);
layoutColumns();
assureVisibleActiveColumn();
}
void DolphinColumnViewContainer::wheelEvent(QWheelEvent* event)
{
// let Ctrl+wheel events propagate to the DolphinView for icon zooming
if ((event->modifiers() & Qt::ControlModifier) == Qt::ControlModifier) {
event->ignore();
} else {
QScrollArea::wheelEvent(event);
}
}
void DolphinColumnViewContainer::moveContentHorizontally(int x)
{
m_contentX = isRightToLeft() ? +x : -x;
layoutColumns();
}
void DolphinColumnViewContainer::updateColumnsBackground(bool active)
{
if (active == m_active) {
return;
}
m_active = active;
// dim the background of the viewport
const QPalette::ColorRole role = viewport()->backgroundRole();
QColor background = viewport()->palette().color(role);
background.setAlpha(0); // make background transparent
QPalette palette = viewport()->palette();
palette.setColor(role, background);
viewport()->setPalette(palette);
foreach (DolphinColumnView* column, m_columns) {
column->updateBackground();
}
}
void DolphinColumnViewContainer::updateActiveUrl()
{
const KUrl activeUrl = m_columns[m_index]->url();
m_dolphinViewController->requestUrlChange(activeUrl);
}
void DolphinColumnViewContainer::slotAssureVisibleActiveColumn()
{
const int viewportWidth = viewport()->width();
const int x = activeColumn()->x();
// When a column that is partly visible gets activated,
// it is useful to also assure that the neighbor column is partly visible.
// This allows the user to scroll to the first/last column without using the
// scrollbar and drag & drop operations to invisible columns.
const int neighborColumnGap = 3 * style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, verticalScrollBar());
const int width = activeColumn()->maximumWidth();
if (x + width > viewportWidth) {
const int newContentX = m_contentX - x - width + viewportWidth;
if (isRightToLeft()) {
m_animation->setFrameRange(m_contentX, newContentX + neighborColumnGap);
} else {
m_animation->setFrameRange(-m_contentX, -newContentX + neighborColumnGap);
}
if (m_animation->state() != QTimeLine::Running) {
m_animation->start();
}
} else if (x < 0) {
const int newContentX = m_contentX - x;
if (isRightToLeft()) {
m_animation->setFrameRange(m_contentX, newContentX - neighborColumnGap);
} else {
m_animation->setFrameRange(-m_contentX, -newContentX - neighborColumnGap);
}
if (m_animation->state() != QTimeLine::Running) {
m_animation->start();
}
}
}
void DolphinColumnViewContainer::assureVisibleActiveColumn()
{
m_assureVisibleActiveColumnTimer->start();
}
void DolphinColumnViewContainer::layoutColumns()
{
// Layout the position of the columns corresponding to their maximum width
QRect emptyViewportRect;
if (isRightToLeft()) {
int columnWidth = m_columns[0]->maximumWidth();
int x = viewport()->width() - columnWidth + m_contentX;
foreach (DolphinColumnView* column, m_columns) {
columnWidth = column->maximumWidth();
column->setGeometry(QRect(x, 0, columnWidth, viewport()->height()));
x -= columnWidth;
}
emptyViewportRect = QRect(0, 0, x + columnWidth, viewport()->height());
} else {
int x = m_contentX;
foreach (DolphinColumnView* column, m_columns) {
const int columnWidth = column->maximumWidth();
column->setGeometry(QRect(x, 0, columnWidth, viewport()->height()));
x += columnWidth;
}
emptyViewportRect = QRect(x, 0, viewport()->width() - x, viewport()->height());
}
// Show an empty viewport if the columns don't cover the whole viewport
if (emptyViewportRect.isValid()) {
m_emptyViewport->show();
m_emptyViewport->setGeometry(emptyViewportRect);
} else {
m_emptyViewport->hide();
}
// Update the horizontal position indicator
int contentWidth = 0;
foreach (DolphinColumnView* column, m_columns) {
contentWidth += column->maximumWidth();
}
const int scrollBarMax = contentWidth - viewport()->width();
const bool updateScrollBar = (horizontalScrollBar()->pageStep() != contentWidth)
|| (horizontalScrollBar()->maximum() != scrollBarMax);
if (updateScrollBar) {
horizontalScrollBar()->setPageStep(contentWidth);
horizontalScrollBar()->setRange(0, scrollBarMax);
}
}
void DolphinColumnViewContainer::requestActivation(DolphinColumnView* column)
{
if (m_dolphinViewController->itemView() != column) {
m_dolphinViewController->setItemView(column);
}
if (focusProxy() != column) {
setFocusProxy(column);
}
if (!column->isActive()) {
// Deactivate the currently active column
if (m_index >= 0) {
m_columns[m_index]->setActive(false);
}
// Get the index of the column that should get activated
int index = 0;
foreach (DolphinColumnView* currColumn, m_columns) {
if (currColumn == column) {
break;
}
++index;
}
Q_ASSERT(index != m_index);
Q_ASSERT(index < m_columns.count());
// Activate the requested column
m_index = index;
m_columns[m_index]->setActive(true);
m_activeUrlTimer->start(); // calls slot updateActiveUrl()
}
assureVisibleActiveColumn();
}
void DolphinColumnViewContainer::removeAllColumns()
{
QList<DolphinColumnView*>::iterator start = m_columns.begin() + 1;
QList<DolphinColumnView*>::iterator end = m_columns.end();
for (QList<DolphinColumnView*>::iterator it = start; it != end; ++it) {
deleteColumn(*it);
}
m_columns.erase(start, end);
m_index = 0;
m_columns[0]->setActive(true);
assureVisibleActiveColumn();
}
void DolphinColumnViewContainer::deleteColumn(DolphinColumnView* column)
{
if (!column) {
return;
}
if (m_dolphinViewController->itemView() == column) {
m_dolphinViewController->setItemView(0);
}
// deleteWhenNotDragSource(column) does not necessarily delete column,
// and we want its preview generator destroyed immediately.
column->hide();
// Prevent automatic destruction of column when this DolphinColumnViewContainer
// is destroyed.
column->setParent(0);
column->disconnect();
if (DragAndDropHelper::instance().isDragSource(column)) {
// The column is a drag source (the feature "Open folders
// during drag operations" is used). Deleting the view
// during an ongoing drag operation is not allowed, so
// this will postponed.
if (m_dragSource) {
// the old stored view is obviously not the drag source anymore
m_dragSource->deleteLater();
m_dragSource = 0;
}
m_dragSource = column;
} else {
delete column;
column = 0;
}
}
#include "dolphincolumnviewcontainer.moc"

View file

@ -1,150 +0,0 @@
/***************************************************************************
* Copyright (C) 2007-2009 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
***************************************************************************/
#ifndef DOLPHINCOLUMNVIEWCONTAINER_H
#define DOLPHINCOLUMNVIEWCONTAINER_H
#include "dolphinview.h"
#include <KUrl>
#include <QList>
#include <QScrollArea>
#include <QString>
class DolphinColumnView;
class DolphinViewController;
class QFrame;
class QTimeLine;
class QTimer;
/**
* @brief Represents a container for columns represented as instances
* of DolphinColumnView.
*
* @see DolphinColumnView
*/
class DolphinColumnViewContainer : public QScrollArea
{
Q_OBJECT
public:
/**
* @param parent Parent widget.
* @param dolphinViewController Allows the DolphinColumnView to control the
* DolphinView in a limited way.
* @param viewModeController Controller that is used by the DolphinView
* to control the DolphinColumnView. The DolphinColumnView
* only has read access to the controller.
* @param model Directory that is shown.
*/
explicit DolphinColumnViewContainer(QWidget* parent,
DolphinViewController* dolphinViewController,
const ViewModeController* viewModeController);
virtual ~DolphinColumnViewContainer();
KUrl rootUrl() const;
QAbstractItemView* activeColumn() const;
/**
* Shows the column which represents the URL \a url. If the column
* is already shown, it gets activated, otherwise it will be created.
*/
void showColumn(const KUrl& url);
protected:
virtual void mousePressEvent(QMouseEvent* event);
virtual void keyPressEvent(QKeyEvent* event);
virtual void resizeEvent(QResizeEvent* event);
virtual void wheelEvent(QWheelEvent* event);
private slots:
/**
* Moves the content of the columns view to represent
* the scrollbar position \a x.
*/
void moveContentHorizontally(int x);
/**
* Updates the background color of the columns to respect
* the current activation state \a active.
*/
void updateColumnsBackground(bool active);
/**
* Tells the Dolphin controller to update the active URL
* to m_activeUrl. The slot is called asynchronously with a
* small delay, as this prevents a flickering when a directory
* from an inactive column gets selected.
*/
void updateActiveUrl();
/**
* Invoked when m_assureVisibleActiveColumnTimer has been exceeded.
* Assures that the currently active column is fully visible
* by adjusting the horizontal position of the content.
*/
void slotAssureVisibleActiveColumn();
private:
/**
* Assures that the currently active column is fully visible
* by adjusting the horizontal position of the content. The
* adjustment is done with a small delay (see
* slotAssureVisibleActiveColumn();
*/
void assureVisibleActiveColumn();
void layoutColumns();
/**
* Request the activation for the column \a column. It is assured
* that the columns gets fully visible by adjusting the horizontal
* position of the content.
*/
void requestActivation(DolphinColumnView* column);
/** Removes all columns except of the root column. */
void removeAllColumns();
/**
* Deletes the column. If the itemview of the controller is set to the column,
* the controllers itemview is set to 0.
*/
void deleteColumn(DolphinColumnView* column);
private:
DolphinViewController* m_dolphinViewController;
const ViewModeController* m_viewModeController;
bool m_active;
int m_index;
int m_contentX;
QList<DolphinColumnView*> m_columns;
QFrame* m_emptyViewport;
QTimeLine* m_animation;
QAbstractItemView* m_dragSource;
QTimer* m_activeUrlTimer;
QTimer* m_assureVisibleActiveColumnTimer;
friend class DolphinColumnView;
};
#endif

View file

@ -1,706 +0,0 @@
/***************************************************************************
* Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at) *
* Copyright (C) 2008 by Simon St. James (kdedevel@etotheipiplusone.com) *
* *
* 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 "additionalinfoaccessor.h"
#include "dolphinmodel.h"
#include "dolphinviewcontroller.h"
#include "dolphinfileitemdelegate.h"
#include "settings/dolphinsettings.h"
#include "dolphinsortfilterproxymodel.h"
#include "dolphinviewautoscroller.h"
#include "draganddrophelper.h"
#include "viewextensionsfactory.h"
#include "viewmodecontroller.h"
#include "viewproperties.h"
#include "zoomlevelinfo.h"
#include "dolphin_detailsmodesettings.h"
#include "dolphin_generalsettings.h"
#include <KDirModel>
#include <KDirLister>
#include <KLocale>
#include <KMenu>
#include <QApplication>
#include <QHeaderView>
#include <QScrollBar>
DolphinDetailsView::DolphinDetailsView(QWidget* parent,
DolphinViewController* dolphinViewController,
const ViewModeController* viewModeController,
DolphinSortFilterProxyModel* proxyModel) :
DolphinTreeView(parent),
m_autoResize(true),
m_dolphinViewController(dolphinViewController),
m_extensionsFactory(0),
m_expandableFoldersAction(0),
m_expandedUrls(),
m_font(),
m_decorationSize()
{
const DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
Q_ASSERT(settings);
Q_ASSERT(dolphinViewController);
Q_ASSERT(viewModeController);
setLayoutDirection(Qt::LeftToRight);
setAcceptDrops(true);
setSortingEnabled(true);
setSelectionBehavior(SelectItems);
setDragDropMode(QAbstractItemView::DragDrop);
setDropIndicatorShown(false);
setAlternatingRowColors(true);
setRootIsDecorated(settings->expandableFolders());
setItemsExpandable(settings->expandableFolders());
setEditTriggers(QAbstractItemView::NoEditTriggers);
setModel(proxyModel);
setMouseTracking(true);
const ViewProperties props(viewModeController->url());
setSortIndicatorSection(props.sorting());
setSortIndicatorOrder(props.sortOrder());
QHeaderView* headerView = header();
connect(headerView, SIGNAL(sectionClicked(int)),
this, SLOT(synchronizeSortingState(int)));
headerView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(headerView, SIGNAL(customContextMenuRequested(const QPoint&)),
this, SLOT(configureSettings(const QPoint&)));
connect(headerView, SIGNAL(sectionResized(int, int, int)),
this, SLOT(slotHeaderSectionResized(int, int, int)));
connect(headerView, SIGNAL(sectionHandleDoubleClicked(int)),
this, SLOT(disableAutoResizing()));
connect(parent, SIGNAL(sortingChanged(DolphinView::Sorting)),
this, SLOT(setSortIndicatorSection(DolphinView::Sorting)));
connect(parent, SIGNAL(sortOrderChanged(Qt::SortOrder)),
this, SLOT(setSortIndicatorOrder(Qt::SortOrder)));
connect(this, SIGNAL(clicked(const QModelIndex&)),
dolphinViewController, SLOT(requestTab(const QModelIndex&)));
if (KGlobalSettings::singleClick()) {
connect(this, SIGNAL(clicked(const QModelIndex&)),
dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
} else {
connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
}
connect(this, SIGNAL(entered(const QModelIndex&)),
this, SLOT(slotEntered(const QModelIndex&)));
connect(this, SIGNAL(viewportEntered()),
dolphinViewController, SLOT(emitViewportEntered()));
connect(viewModeController, SIGNAL(zoomLevelChanged(int)),
this, SLOT(setZoomLevel(int)));
connect(dolphinViewController->view(), SIGNAL(additionalInfoChanged()),
this, SLOT(updateColumnVisibility()));
connect(viewModeController, SIGNAL(activationChanged(bool)),
this, SLOT(slotActivationChanged(bool)));
if (settings->useSystemFont()) {
m_font = KGlobalSettings::generalFont();
} else {
m_font = QFont(settings->fontFamily(),
qRound(settings->fontSize()),
settings->fontWeight(),
settings->italicFont());
m_font.setPointSizeF(settings->fontSize());
}
setVerticalScrollMode(QTreeView::ScrollPerPixel);
setHorizontalScrollMode(QTreeView::ScrollPerPixel);
const DolphinView* view = dolphinViewController->view();
connect(view, SIGNAL(showPreviewChanged()),
this, SLOT(slotShowPreviewChanged()));
viewport()->installEventFilter(this);
connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)),
this, SLOT(slotGlobalSettingsChanged(int)));
m_expandableFoldersAction = new QAction(i18nc("@option:check", "Expandable Folders"), this);
m_expandableFoldersAction->setCheckable(true);
connect(m_expandableFoldersAction, SIGNAL(toggled(bool)),
this, SLOT(setFoldersExpandable(bool)));
connect(this, SIGNAL(expanded(const QModelIndex&)), this, SLOT(slotExpanded(const QModelIndex&)));
connect(this, SIGNAL(collapsed(const QModelIndex&)), this, SLOT(slotCollapsed(const QModelIndex&)));
updateDecorationSize(view->showPreview());
m_extensionsFactory = new ViewExtensionsFactory(this, dolphinViewController, viewModeController);
m_extensionsFactory->fileItemDelegate()->setMinimizedNameColumn(true);
KDirLister *dirLister = qobject_cast<KDirModel*>(proxyModel->sourceModel())->dirLister();
connect(dirLister, SIGNAL(newItems(KFileItemList)), this, SLOT(resizeColumns()));
}
DolphinDetailsView::~DolphinDetailsView()
{
}
QSet<KUrl> DolphinDetailsView::expandedUrls() const
{
return m_expandedUrls;
}
bool DolphinDetailsView::event(QEvent* event)
{
if (event->type() == QEvent::Polish) {
header()->setResizeMode(QHeaderView::Interactive);
updateColumnVisibility();
}
return DolphinTreeView::event(event);
}
QStyleOptionViewItem DolphinDetailsView::viewOptions() const
{
QStyleOptionViewItem viewOptions = DolphinTreeView::viewOptions();
viewOptions.font = m_font;
viewOptions.fontMetrics = QFontMetrics(m_font);
viewOptions.showDecorationSelected = true;
viewOptions.decorationSize = m_decorationSize;
return viewOptions;
}
void DolphinDetailsView::contextMenuEvent(QContextMenuEvent* event)
{
DolphinTreeView::contextMenuEvent(event);
DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
m_expandableFoldersAction->setChecked(settings->expandableFolders());
m_dolphinViewController->triggerContextMenuRequest(event->pos(),
QList<QAction*>() << m_expandableFoldersAction);
}
void DolphinDetailsView::mousePressEvent(QMouseEvent* event)
{
m_dolphinViewController->requestActivation();
DolphinTreeView::mousePressEvent(event);
const QModelIndex index = indexAt(event->pos());
if (!index.isValid() || (index.column() != DolphinModel::Name)) {
// The mouse press is done somewhere outside the filename column
if (QApplication::mouseButtons() & Qt::MidButton) {
m_dolphinViewController->replaceUrlByClipboard();
}
}
}
void DolphinDetailsView::startDrag(Qt::DropActions supportedActions)
{
DragAndDropHelper::instance().startDrag(this, supportedActions, m_dolphinViewController);
DolphinTreeView::startDrag(supportedActions);
}
void DolphinDetailsView::dragEnterEvent(QDragEnterEvent* event)
{
event->acceptProposedAction();
DolphinTreeView::dragEnterEvent(event);
}
void DolphinDetailsView::dragMoveEvent(QDragMoveEvent* event)
{
DolphinTreeView::dragMoveEvent(event);
event->acceptProposedAction();
}
void DolphinDetailsView::dropEvent(QDropEvent* event)
{
const QModelIndex index = indexAt(event->pos());
KFileItem item;
if (index.isValid() && (index.column() == DolphinModel::Name)) {
item = m_dolphinViewController->itemForIndex(index);
}
m_dolphinViewController->indicateDroppedUrls(item, event);
DolphinTreeView::dropEvent(event);
}
void DolphinDetailsView::keyPressEvent(QKeyEvent* event)
{
DolphinTreeView::keyPressEvent(event);
m_dolphinViewController->handleKeyPressEvent(event);
}
void DolphinDetailsView::resizeEvent(QResizeEvent* event)
{
DolphinTreeView::resizeEvent(event);
if (m_autoResize) {
resizeColumns();
}
}
void DolphinDetailsView::wheelEvent(QWheelEvent* event)
{
const int step = m_decorationSize.height();
verticalScrollBar()->setSingleStep(step);
DolphinTreeView::wheelEvent(event);
}
void DolphinDetailsView::currentChanged(const QModelIndex& current, const QModelIndex& previous)
{
m_extensionsFactory->handleCurrentIndexChange(current, previous);
DolphinTreeView::currentChanged(current, previous);
// If folders are expanded, the width which is available for editing may have changed
// because it depends on the level of the current item in the folder hierarchy.
adjustMaximumSizeForEditing(current);
}
bool DolphinDetailsView::eventFilter(QObject* watched, QEvent* event)
{
if ((watched == viewport()) && (event->type() == QEvent::Leave)) {
// If the mouse is above an item and moved very fast outside the widget,
// no viewportEntered() signal might be emitted although the mouse has been moved
// above the viewport.
m_dolphinViewController->emitViewportEntered();
}
return DolphinTreeView::eventFilter(watched, event);
}
QRect DolphinDetailsView::visualRect(const QModelIndex& index) const
{
QRect rect = DolphinTreeView::visualRect(index);
const KFileItem item = m_dolphinViewController->itemForIndex(index);
if (!item.isNull()) {
const int width = DolphinFileItemDelegate::nameColumnWidth(item.text(), viewOptions());
if (width < rect.width()) {
rect.setWidth(width);
}
}
return rect;
}
bool DolphinDetailsView::acceptsDrop(const QModelIndex& index) const
{
if (index.isValid() && (index.column() == DolphinModel::Name)) {
// Accept drops above directories
const KFileItem item = m_dolphinViewController->itemForIndex(index);
return !item.isNull() && item.isDir();
}
return false;
}
void DolphinDetailsView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end)
{
removeExpandedIndexes(parent, start, end);
DolphinTreeView::rowsAboutToBeRemoved(parent, start, end);
}
void DolphinDetailsView::setSortIndicatorSection(DolphinView::Sorting sorting)
{
header()->setSortIndicator(sorting, header()->sortIndicatorOrder());
}
void DolphinDetailsView::setSortIndicatorOrder(Qt::SortOrder sortOrder)
{
header()->setSortIndicator(header()->sortIndicatorSection(), sortOrder);
}
void DolphinDetailsView::synchronizeSortingState(int column)
{
// The sorting has already been changed in QTreeView if this slot is
// invoked, but Dolphin is not informed about this.
DolphinView::Sorting sorting = DolphinSortFilterProxyModel::sortingForColumn(column);
const Qt::SortOrder sortOrder = header()->sortIndicatorOrder();
m_dolphinViewController->indicateSortingChange(sorting);
m_dolphinViewController->indicateSortOrderChange(sortOrder);
}
void DolphinDetailsView::slotEntered(const QModelIndex& index)
{
if (index.column() == DolphinModel::Name) {
m_dolphinViewController->emitItemEntered(index);
} else {
m_dolphinViewController->emitViewportEntered();
}
}
void DolphinDetailsView::setZoomLevel(int level)
{
const int size = ZoomLevelInfo::iconSizeForZoomLevel(level);
DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
const bool showPreview = m_dolphinViewController->view()->showPreview();
if (showPreview) {
settings->setPreviewSize(size);
} else {
settings->setIconSize(size);
}
updateDecorationSize(showPreview);
}
void DolphinDetailsView::slotShowPreviewChanged()
{
const DolphinView* view = m_dolphinViewController->view();
updateDecorationSize(view->showPreview());
}
void DolphinDetailsView::configureSettings(const QPoint& pos)
{
KMenu popup(this);
popup.addTitle(i18nc("@title:menu", "Columns"));
// Add checkbox items for each column
QHeaderView* headerView = header();
const int columns = model()->columnCount();
for (int i = 0; i < columns; ++i) {
const int logicalIndex = headerView->logicalIndex(i);
const QString text = model()->headerData(logicalIndex, Qt::Horizontal).toString();
if (!text.isEmpty()) {
QAction* action = popup.addAction(text);
action->setCheckable(true);
action->setChecked(!headerView->isSectionHidden(logicalIndex));
action->setData(logicalIndex);
action->setEnabled(logicalIndex != DolphinModel::Name);
}
}
popup.addSeparator();
QAction* activatedAction = popup.exec(header()->mapToGlobal(pos));
if (activatedAction) {
const bool show = activatedAction->isChecked();
const int columnIndex = activatedAction->data().toInt();
KFileItemDelegate::InformationList list = m_dolphinViewController->view()->additionalInfo();
const KFileItemDelegate::Information info = infoForColumn(columnIndex);
if (show) {
Q_ASSERT(!list.contains(info));
list.append(info);
} else {
Q_ASSERT(list.contains(info));
const int index = list.indexOf(info);
list.removeAt(index);
}
m_dolphinViewController->indicateAdditionalInfoChange(list);
setColumnHidden(columnIndex, !show);
resizeColumns();
}
}
void DolphinDetailsView::updateColumnVisibility()
{
QHeaderView* headerView = header();
disconnect(headerView, SIGNAL(sectionMoved(int, int, int)),
this, SLOT(saveColumnPositions()));
const DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
const QList<int> columnPositions = settings->columnPositions();
const KFileItemDelegate::InformationList list = m_dolphinViewController->view()->additionalInfo();
for (int i = DolphinModel::Name; i < DolphinModel::ExtraColumnCount; ++i) {
const KFileItemDelegate::Information info = infoForColumn(i);
const bool hide = !list.contains(info) && (i != DolphinModel::Name);
if (isColumnHidden(i) != hide) {
setColumnHidden(i, hide);
}
// If the list columnPositions has been written by an older Dolphin version,
// its length might be smaller than DolphinModel::ExtraColumnCount. Therefore,
// we have to check if item number i exists before accessing it.
if (i < columnPositions.length()) {
const int position = columnPositions[i];
// The position might be outside the correct range if the list columnPositions
// has been written by a newer Dolphin version with more columns.
if (position < DolphinModel::ExtraColumnCount) {
const int from = headerView->visualIndex(i);
headerView->moveSection(from, position);
}
}
}
resizeColumns();
connect(headerView, SIGNAL(sectionMoved(int, int, int)),
this, SLOT(saveColumnPositions()));
}
void DolphinDetailsView::resizeColumns()
{
// Using the resize mode QHeaderView::ResizeToContents is too slow (it takes
// around 3 seconds for each (!) resize operation when having > 10000 items).
// This gets a problem especially when opening large directories, where several
// resize operations are received for showing the currently available items during
// loading (the application hangs around 20 seconds when loading > 10000 items).
QHeaderView* headerView = header();
const int rowCount = model()->rowCount();
QFontMetrics fontMetrics(viewport()->font());
const int horizontalGap = fontMetrics.height();
// Define the maximum number of rows, where an exact (but expensive) calculation
// of the widths is done.
const int maxRowCount = 200;
// Calculate the required with for each column and store it in columnWidth[]
int columnWidth[DolphinModel::ExtraColumnCount];
for (int column = 0; column < DolphinModel::ExtraColumnCount; ++column) {
columnWidth[column] = 0;
if (!isColumnHidden(column)) {
// Calculate the required width for the current column and consider only
// up to maxRowCount columns for performance reasons
if (rowCount > 0) {
const int count = qMin(rowCount, maxRowCount);
for (int row = 0; row < count; ++row) {
const QModelIndex index = model()->index(row, column);
QString text;
if (column == DolphinModel::Size) {
// This is a workaround as KFileItemDelegate::sizeHint() does not
// work in a way that is required for calculating the size.
const QAbstractProxyModel* proxyModel = qobject_cast<const QAbstractProxyModel*>(model());
const KDirModel* dirModel = qobject_cast<const KDirModel*>(proxyModel->sourceModel());
const QModelIndex dirIndex = proxyModel->mapToSource(index);
text = itemSizeString(dirIndex, dirModel->itemForIndex(dirIndex));
} else {
text = model()->data(index).toString();
}
const int width = fontMetrics.width(text) + horizontalGap;
if (width > columnWidth[column]) {
columnWidth[column] = width;
}
}
}
// Assure that the required width is sufficient for the header too
const int logicalIndex = headerView->logicalIndex(column);
const QString headline = model()->headerData(logicalIndex, Qt::Horizontal).toString();
const int headlineWidth = fontMetrics.width(headline) + horizontalGap;
columnWidth[column] = qMax(columnWidth[column], headlineWidth);
}
}
// Resize all columns except of the name column
int requiredWidth = 0;
for (int column = KDirModel::Size; column < DolphinModel::ExtraColumnCount; ++column) {
if (!isColumnHidden(column)) {
requiredWidth += columnWidth[column];
headerView->resizeSection(column, columnWidth[column]);
}
}
// Resize the name column in a way that the whole available width is used
columnWidth[KDirModel::Name] = viewport()->width() - requiredWidth;
const int minNameWidth = 300;
if (columnWidth[KDirModel::Name] < minNameWidth) {
columnWidth[KDirModel::Name] = minNameWidth;
if ((rowCount > 0) && (rowCount < maxRowCount)) {
// Try to decrease the name column width without clipping any text
const int nameWidth = sizeHintForColumn(DolphinModel::Name);
if (nameWidth + requiredWidth <= viewport()->width()) {
columnWidth[KDirModel::Name] = viewport()->width() - requiredWidth;
} else if (nameWidth < minNameWidth) {
columnWidth[KDirModel::Name] = nameWidth;
}
}
}
headerView->resizeSection(KDirModel::Name, columnWidth[KDirModel::Name]);
}
void DolphinDetailsView::saveColumnPositions()
{
QList<int> columnPositions;
for (int i = DolphinModel::Name; i < DolphinModel::ExtraColumnCount; ++i) {
columnPositions.append(header()->visualIndex(i));
}
DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
settings->setColumnPositions(columnPositions);
}
void DolphinDetailsView::slotHeaderSectionResized(int logicalIndex, int oldSize, int newSize)
{
Q_UNUSED(logicalIndex);
Q_UNUSED(oldSize);
Q_UNUSED(newSize);
// If the user changes the size of the headers, the autoresize feature should be
// turned off. As there is no dedicated interface to find out whether the header
// section has been resized by the user or by a resize event, another approach is used.
// Attention: Take care when changing the if-condition to verify that there is no
// regression in combination with bug 178630 (see fix in comment #8).
if ((QApplication::mouseButtons() & Qt::LeftButton) && header()->underMouse()) {
disableAutoResizing();
}
adjustMaximumSizeForEditing(currentIndex());
}
void DolphinDetailsView::slotActivationChanged(bool active)
{
setAlternatingRowColors(active);
}
void DolphinDetailsView::disableAutoResizing()
{
m_autoResize = false;
}
void DolphinDetailsView::requestActivation()
{
m_dolphinViewController->requestActivation();
}
void DolphinDetailsView::slotGlobalSettingsChanged(int category)
{
Q_UNUSED(category);
const DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
Q_ASSERT(settings);
if (settings->useSystemFont()) {
m_font = KGlobalSettings::generalFont();
}
// Disconnect then reconnect, since the settings have been changed, the connection requirements may have also.
disconnect(this, SIGNAL(clicked(QModelIndex)), m_dolphinViewController, SLOT(triggerItem(QModelIndex)));
disconnect(this, SIGNAL(doubleClicked(QModelIndex)), m_dolphinViewController, SLOT(triggerItem(QModelIndex)));
if (KGlobalSettings::singleClick()) {
connect(this, SIGNAL(clicked(QModelIndex)), m_dolphinViewController, SLOT(triggerItem(QModelIndex)));
} else {
connect(this, SIGNAL(doubleClicked(QModelIndex)), m_dolphinViewController, SLOT(triggerItem(QModelIndex)));
}
}
void DolphinDetailsView::setFoldersExpandable(bool expandable)
{
if (!expandable) {
// Collapse all expanded folders, as QTreeView::setItemsExpandable(false)
// does not do this task
const int rowCount = model()->rowCount();
for (int row = 0; row < rowCount; ++row) {
setExpanded(model()->index(row, 0), false);
}
}
DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
settings->setExpandableFolders(expandable);
setRootIsDecorated(expandable);
setItemsExpandable(expandable);
// The width of the space which is available for editing has changed
// because of the (dis)appearance of the expanding toggles
adjustMaximumSizeForEditing(currentIndex());
}
void DolphinDetailsView::slotExpanded(const QModelIndex& index)
{
KFileItem item = m_dolphinViewController->itemForIndex(index);
if (!item.isNull()) {
m_expandedUrls.insert(item.url());
}
}
void DolphinDetailsView::slotCollapsed(const QModelIndex& index)
{
KFileItem item = m_dolphinViewController->itemForIndex(index);
if (!item.isNull()) {
m_expandedUrls.remove(item.url());
}
}
void DolphinDetailsView::removeExpandedIndexes(const QModelIndex& parent, int start, int end)
{
if (m_expandedUrls.isEmpty()) {
return;
}
for (int row = start; row <= end; row++) {
const QModelIndex index = model()->index(row, 0, parent);
if (isExpanded(index)) {
slotCollapsed(index);
removeExpandedIndexes(index, 0, model()->rowCount(index) - 1);
}
}
}
void DolphinDetailsView::updateDecorationSize(bool showPreview)
{
DetailsModeSettings* settings = DolphinSettings::instance().detailsModeSettings();
const int iconSize = showPreview ? settings->previewSize() : settings->iconSize();
setIconSize(QSize(iconSize, iconSize));
m_decorationSize = QSize(iconSize, iconSize);
if (m_extensionsFactory) {
// The old maximumSize used by KFileItemDelegate is not valid any more after the icon size change.
// It must be discarded before doItemsLayout() is called (see bug 234600).
m_extensionsFactory->fileItemDelegate()->setMaximumSize(QSize());
}
doItemsLayout();
// Calculate the new maximumSize for KFileItemDelegate after the icon size change.
QModelIndex current = currentIndex();
if (current.isValid()) {
adjustMaximumSizeForEditing(current);
}
}
KFileItemDelegate::Information DolphinDetailsView::infoForColumn(int columnIndex) const
{
return AdditionalInfoAccessor::instance().keyForColumn(columnIndex);
}
void DolphinDetailsView::adjustMaximumSizeForEditing(const QModelIndex& index)
{
// Make sure that the full width of the "Name" column is available for "Rename Inline".
// Before we do that, we have to check if m_extensionsFactory has been initialised because
// it is possible that we end up here before the constructor is finished (see bug 257035)
if (m_extensionsFactory) {
m_extensionsFactory->fileItemDelegate()->setMaximumSize(QTreeView::visualRect(index).size());
}
}
QString DolphinDetailsView::itemSizeString(const QModelIndex& index, const KFileItem& item) const
{
// The following code has been copied from KFileItemDelegate::Private::itemSize()
// Copyright (c) 2006-2007, 2008 Fredrik Höglund <fredrik@kde.org>
// Ideally this should be handled by KFileItemDelegate::sizeHint().
if (item.isFile()) {
return KGlobal::locale()->formatByteSize(item.size());
}
// Return the number of items in the directory
const QVariant value = index.data(KDirModel::ChildCountRole);
const int count = value.type() == QVariant::Int ? value.toInt() : KDirModel::ChildCountUnknown;
if (count == KDirModel::ChildCountUnknown) {
return QString();
}
return i18ncp("Items in a folder", "1 item", "%1 items", count);
}
#include "dolphindetailsview.moc"

View file

@ -1,224 +0,0 @@
/***************************************************************************
* Copyright (C) 2006-2010 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 *
***************************************************************************/
#ifndef DOLPHINDETAILSVIEW_H
#define DOLPHINDETAILSVIEW_H
#include "dolphintreeview.h"
#include <QTreeView>
#include <libdolphin_export.h>
#include <views/dolphinview.h>
class DolphinViewController;
class DolphinSortFilterProxyModel;
class ViewExtensionsFactory;
/**
* @brief Represents the details view which shows the name, size,
* date, permissions, owner and group of an item.
*
* The width of the columns is automatically adjusted in a way
* that full available width of the view is used by stretching the width
* of the name column.
*/
class LIBDOLPHINPRIVATE_EXPORT DolphinDetailsView : public DolphinTreeView
{
Q_OBJECT
public:
/**
* @param parent Parent widget.
* @param dolphinViewController Allows the DolphinDetailsView to control the
* DolphinView in a limited way.
* @param viewModeController Controller that is used by the DolphinView
* to control the DolphinDetailsView. The DolphinDetailsView
* only has read access to the controller.
* @param model Directory that is shown.
*/
explicit DolphinDetailsView(QWidget* parent,
DolphinViewController* dolphinViewController,
const ViewModeController* viewModeController,
DolphinSortFilterProxyModel* model);
virtual ~DolphinDetailsView();
/**
* Returns a set containing the URLs of all expanded items.
*/
QSet<KUrl> expandedUrls() const;
public:
virtual QRect visualRect(const QModelIndex& index) const;
protected:
virtual bool event(QEvent* event);
virtual QStyleOptionViewItem viewOptions() const;
virtual void contextMenuEvent(QContextMenuEvent* event);
virtual void mousePressEvent(QMouseEvent* event);
virtual void startDrag(Qt::DropActions supportedActions);
virtual void dragEnterEvent(QDragEnterEvent* event);
virtual void dragMoveEvent(QDragMoveEvent* event);
virtual void dropEvent(QDropEvent* event);
virtual void keyPressEvent(QKeyEvent* event);
virtual void resizeEvent(QResizeEvent* event);
virtual void wheelEvent(QWheelEvent* event);
virtual void currentChanged(const QModelIndex& current, const QModelIndex& previous);
virtual bool eventFilter(QObject* watched, QEvent* event);
virtual bool acceptsDrop(const QModelIndex& index) const;
protected slots:
virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
private slots:
/**
* Sets the sort indicator section of the header view
* corresponding to \a sorting.
*/
void setSortIndicatorSection(DolphinView::Sorting sorting);
/**
* Sets the sort indicator order of the header view
* corresponding to \a sortOrder.
*/
void setSortIndicatorOrder(Qt::SortOrder sortOrder);
/**
* Synchronizes the sorting state of the Dolphin menu 'View -> Sort'
* with the current state of the details view.
* @param column Index of the current sorting column.
*/
void synchronizeSortingState(int column);
/**
* Is invoked when the mouse cursor has entered an item. The controller
* gets informed to emit the itemEntered() signal if the mouse cursor
* is above the name column. Otherwise the controller gets informed
* to emit the itemViewportEntered() signal (all other columns should
* behave as viewport area).
*/
void slotEntered(const QModelIndex& index);
void setZoomLevel(int level);
void slotShowPreviewChanged();
/**
* Opens a context menu at the position \a pos and allows to
* configure the visibility of the header columns and whether
* expandable folders should be shown.
*/
void configureSettings(const QPoint& pos);
/**
* Updates the visibilty state of columns and their order.
*/
void updateColumnVisibility();
/**
* Resizes all columns in a way to use the whole available width of the view.
*/
void resizeColumns();
/**
* Saves order of the columns as global setting.
*/
void saveColumnPositions();
/**
* Disables the automatical resizing of columns, if the user has resized the columns
* with the mouse.
*/
void slotHeaderSectionResized(int logicalIndex, int oldSize, int newSize);
/**
* Changes the alternating row colors setting depending from
* the activation state \a active.
*/
void slotActivationChanged(bool active);
/**
* Disables the automatical resizing of the columns. Per default all columns
* are resized to use the maximum available width of the view as good as possible.
*/
void disableAutoResizing();
void requestActivation();
void slotGlobalSettingsChanged(int category);
/**
* If \a expandable is true, the details view acts as tree view.
* The current expandable state is remembered in the settings.
*/
void setFoldersExpandable(bool expandable);
/**
* These slots update the list of expanded items.
*/
void slotExpanded(const QModelIndex& index);
void slotCollapsed(const QModelIndex& index);
private:
/**
* Removes the URLs corresponding to the children of \a index in the rows
* between \a start and \a end inclusive from the set of expanded URLs.
*/
void removeExpandedIndexes(const QModelIndex& parent, int start, int end);
/**
* Updates the size of the decoration dependent on the
* icon size of the DetailsModeSettings. The controller
* will get informed about possible zoom in/zoom out
* operations.
*/
void updateDecorationSize(bool showPreview);
KFileItemDelegate::Information infoForColumn(int columnIndex) const;
/**
* Sets the maximum size available for editing in the delegate.
*/
void adjustMaximumSizeForEditing(const QModelIndex& index);
/**
* Helper method for DolphinDetailsView::resizeColumns(): Returns the
* string representation of the size-value for the given index.
*/
QString itemSizeString(const QModelIndex& index, const KFileItem& item) const;
private:
bool m_autoResize; // if true, the columns are resized automatically to the available width
DolphinViewController* m_dolphinViewController;
ViewExtensionsFactory* m_extensionsFactory;
QAction* m_expandableFoldersAction;
// A set containing the URLs of all currently expanded folders.
// We cannot use a QSet<QModelIndex> because a QModelIndex is not guaranteed to remain valid over time.
// Also a QSet<QPersistentModelIndex> does not work as expected because it is not guaranteed that
// subsequent expand/collapse events of the same file item will yield the same QPersistentModelIndex.
QSet<KUrl> m_expandedUrls;
QFont m_font;
QSize m_decorationSize;
// For unit tests
friend class DolphinDetailsViewTest;
};
#endif

View file

@ -1,87 +0,0 @@
/***************************************************************************
* Copyright (C) 2009 by Frank Reininghaus (frank78ac@googlemail.com) *
* *
* 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 "dolphindetailsviewexpander.h"
#include "dolphindetailsview.h"
#include "dolphinmodel.h"
#include "dolphinsortfilterproxymodel.h"
#include <KDirLister>
#include <KDirModel>
DolphinDetailsViewExpander::DolphinDetailsViewExpander(DolphinDetailsView* parent,
const QSet<KUrl>& urlsToExpand) :
QObject(parent),
m_detailsView(parent),
m_dirLister(0),
m_dolphinModel(0),
m_proxyModel(0)
{
Q_ASSERT(parent);
m_proxyModel = qobject_cast<const DolphinSortFilterProxyModel*>(parent->model());
Q_ASSERT(m_proxyModel);
m_dolphinModel = qobject_cast<const DolphinModel*>(m_proxyModel->sourceModel());
Q_ASSERT(m_dolphinModel);
m_dirLister = m_dolphinModel->dirLister();
Q_ASSERT(m_dirLister);
// The URLs must be sorted. E.g. /home/user/ cannot be expanded before /home/
// because it is not known to the dir model before.
m_urlsToExpand = urlsToExpand.toList();
qSort(m_urlsToExpand);
// The dir lister must have completed the folder listing before a subfolder can be expanded.
connect(m_dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted()));
}
DolphinDetailsViewExpander::~DolphinDetailsViewExpander()
{
}
void DolphinDetailsViewExpander::stop()
{
disconnect(m_dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted()));
deleteLater();
}
void DolphinDetailsViewExpander::slotDirListerCompleted()
{
QModelIndex dirIndex;
while(!m_urlsToExpand.isEmpty() && !dirIndex.isValid()) {
const KUrl url = m_urlsToExpand.takeFirst();
dirIndex = m_dolphinModel->indexForUrl(url);
}
if(dirIndex.isValid()) {
// A valid model index was found. Note that only one item is expanded in each call of this slot
// because expanding any item will trigger KDirLister::openUrl(...) via KDirModel::fetchMore(...),
// and we can only continue when the dir lister is done.
const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
m_detailsView->expand(proxyIndex);
}
else {
emit completed();
stop();
}
}

View file

@ -1,77 +0,0 @@
/***************************************************************************
* Copyright (C) 2009 by Frank Reininghaus (frank78ac@googlemail.com) *
* *
* 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 *
***************************************************************************/
#ifndef DOLPHINDETAILSVIEWEXPANDER_H
#define DOLPHINDETAILSVIEWEXPANDER_H
#include <QObject>
#include <QSet>
#include <QList>
class DolphinDetailsView;
class KUrl;
class KDirLister;
class DolphinModel;
class DolphinSortFilterProxyModel;
/**
* @brief Expands a given set of subfolders in collaboration with the dir lister and the dir model.
*
* Note that only one subfolder can be expanded at a time. Each expansion triggers KDirLister::openUrl(...),
* and further expansions can only be done the next time the dir lister emits its completed() signal.
*/
class DolphinDetailsViewExpander : public QObject
{
Q_OBJECT
public:
explicit DolphinDetailsViewExpander(DolphinDetailsView* parent,
const QSet<KUrl>& urlsToExpand);
virtual ~DolphinDetailsViewExpander();
/**
* Stops the expansion and deletes the object via deleteLater().
*/
void stop();
private slots:
/**
* This slot is invoked every time the dir lister has completed a listing.
* It expands the first URL from the list m_urlsToExpand that can be found in the dir model.
* If the list is empty, stop() is called.
*/
void slotDirListerCompleted();
signals:
/**
* Is emitted when the expander has finished expanding URLs in the details view.
*/
void completed();
private:
QList<KUrl> m_urlsToExpand;
DolphinDetailsView* m_detailsView;
const KDirLister* m_dirLister;
const DolphinModel* m_dolphinModel;
const DolphinSortFilterProxyModel* m_proxyModel;
};
#endif

View file

@ -21,8 +21,8 @@
#include <KLocale>
#include <KIO/JobClasses>
DolphinDirLister::DolphinDirLister() :
KDirLister()
DolphinDirLister::DolphinDirLister(QObject* parent) :
KDirLister(parent)
{
setAutoErrorHandlingEnabled(false, 0);
}

View file

@ -32,7 +32,7 @@ class LIBDOLPHINPRIVATE_EXPORT DolphinDirLister : public KDirLister
Q_OBJECT
public:
DolphinDirLister();
DolphinDirLister(QObject* parent = 0);
virtual ~DolphinDirLister();
signals:

View file

@ -1,221 +0,0 @@
/***************************************************************************
* Copyright (C) 2008 by Peter Penz <peter.penz19@gmail.com> *
* *
* 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 "dolphinfileitemdelegate.h"
#include "dolphinmodel.h"
#include <KColorScheme>
#include <KFileItem>
#include <KGlobalSettings>
#include <KIcon>
#include <KIconLoader>
#include <KStringHandler>
#include <QAbstractItemModel>
#include <QAbstractProxyModel>
#include <QFontMetrics>
#include <QPalette>
#include <QPainter>
#include <QStyleOptionViewItemV4>
DolphinFileItemDelegate::DolphinFileItemDelegate(QObject* parent) :
KFileItemDelegate(parent),
m_hasMinimizedNameColumn(false),
m_cachedSize(),
m_cachedEmblems(),
m_cachedInactiveTextColorDirty(true)
{
setJobTransfersVisible(true);
connect(KGlobalSettings::self(), SIGNAL(kdisplayPaletteChanged()), this, SLOT(handleDisplayPaletteChange()));
}
DolphinFileItemDelegate::~DolphinFileItemDelegate()
{
}
void DolphinFileItemDelegate::paint(QPainter* painter,
const QStyleOptionViewItem& option,
const QModelIndex& index) const
{
const QAbstractProxyModel* proxyModel = static_cast<const QAbstractProxyModel*>(index.model());
const DolphinModel* dolphinModel = static_cast<const DolphinModel*>(proxyModel->sourceModel());
const bool isNameColumn = (index.column() == KDirModel::Name);
QStyleOptionViewItemV4 opt(option);
if (m_hasMinimizedNameColumn && isNameColumn) {
adjustOptionWidth(opt, proxyModel, dolphinModel, index);
}
if (!isNameColumn) {
// Use the inactive text color for all columns except the name column. This indicates for the user that
// hovering other columns does not change the actions context.
QPalette palette = opt.palette;
if (m_cachedInactiveTextColorDirty) {
m_cachedInactiveTextColor = KColorScheme(QPalette::Active).foreground(KColorScheme::InactiveText).color();
m_cachedInactiveTextColorDirty = false;
}
palette.setColor(QPalette::Text, m_cachedInactiveTextColor);
opt.palette = palette;
}
if (dolphinModel->hasVersionData() && isNameColumn) {
// The currently shown items are under revision control. Show the current revision
// state by adding an emblem and changing the text tintColor.
const QModelIndex dirIndex = proxyModel->mapToSource(index);
const QModelIndex revisionIndex = dolphinModel->index(dirIndex.row(), DolphinModel::Version, dirIndex.parent());
const QVariant data = dolphinModel->data(revisionIndex, Qt::DecorationRole);
const KVersionControlPlugin::VersionState state = static_cast<KVersionControlPlugin::VersionState>(data.toInt());
adjustOptionTextColor(opt, state);
KFileItemDelegate::paint(painter, opt, index);
if (state != KVersionControlPlugin::UnversionedVersion) {
const QRect rect = iconRect(option, index);
const QPixmap emblem = emblemForState(state, rect.size());
painter->drawPixmap(rect.x(), rect.y() + rect.height() - emblem.height(), emblem);
}
} else {
KFileItemDelegate::paint(painter, opt, index);
}
}
int DolphinFileItemDelegate::nameColumnWidth(const QString& name, const QStyleOptionViewItem& option)
{
QFontMetrics fontMetrics(option.font);
int width = option.decorationSize.width() + fontMetrics.width(KStringHandler::preProcessWrap(name)) + 16;
const int defaultWidth = option.rect.width();
if ((defaultWidth > 0) && (defaultWidth < width)) {
width = defaultWidth;
}
return width;
}
void DolphinFileItemDelegate::handleDisplayPaletteChange()
{
m_cachedInactiveTextColorDirty = true;
}
void DolphinFileItemDelegate::adjustOptionWidth(QStyleOptionViewItemV4& option,
const QAbstractProxyModel* proxyModel,
const DolphinModel* dolphinModel,
const QModelIndex& index)
{
const QModelIndex dirIndex = proxyModel->mapToSource(index);
const KFileItem item = dolphinModel->itemForIndex(dirIndex);
if (!item.isNull()) {
// symbolic links are displayed in an italic font
if (item.isLink()) {
option.font.setItalic(true);
}
const int width = nameColumnWidth(item.text(), option);
option.rect.setWidth(width);
}
}
void DolphinFileItemDelegate::adjustOptionTextColor(QStyleOptionViewItemV4& option,
KVersionControlPlugin::VersionState state)
{
QColor tintColor;
// Using hardcoded colors is generally a bad idea. In this case the colors just act
// as tint colors and are mixed with the current set text color. The tint colors
// have been optimized for the base colors of the corresponding Oxygen emblems.
switch (state) {
case KVersionControlPlugin::UpdateRequiredVersion: tintColor = Qt::yellow; break;
case KVersionControlPlugin::LocallyModifiedUnstagedVersion: tintColor = Qt::darkGreen; break;
case KVersionControlPlugin::LocallyModifiedVersion: tintColor = Qt::green; break;
case KVersionControlPlugin::AddedVersion: tintColor = Qt::green; break;
case KVersionControlPlugin::RemovedVersion: tintColor = Qt::darkRed; break;
case KVersionControlPlugin::ConflictingVersion: tintColor = Qt::red; break;
case KVersionControlPlugin::UnversionedVersion:
case KVersionControlPlugin::NormalVersion:
default:
// use the default text color
return;
}
QPalette palette = option.palette;
const QColor textColor = palette.color(QPalette::Text);
tintColor = QColor((tintColor.red() + textColor.red()) / 2,
(tintColor.green() + textColor.green()) / 2,
(tintColor.blue() + textColor.blue()) / 2,
(tintColor.alpha() + textColor.alpha()) / 2);
palette.setColor(QPalette::Text, tintColor);
option.palette = palette;
}
QPixmap DolphinFileItemDelegate::emblemForState(KVersionControlPlugin::VersionState state, const QSize& size) const
{
Q_ASSERT(state <= KVersionControlPlugin::LocallyModifiedUnstagedVersion);
if (m_cachedSize != size) {
m_cachedSize = size;
const int iconHeight = size.height();
int emblemHeight = KIconLoader::SizeSmall;
if (iconHeight >= KIconLoader::SizeEnormous) {
emblemHeight = KIconLoader::SizeMedium;
} else if (iconHeight >= KIconLoader::SizeLarge) {
emblemHeight = KIconLoader::SizeSmallMedium;
} else if (iconHeight >= KIconLoader::SizeMedium) {
emblemHeight = KIconLoader::SizeSmall;
} else {
emblemHeight = KIconLoader::SizeSmall / 2;
}
const QSize emblemSize(emblemHeight, emblemHeight);
for (int i = KVersionControlPlugin::NormalVersion; i <= KVersionControlPlugin::LocallyModifiedUnstagedVersion; ++i) {
QString iconName;
switch (i) {
case KVersionControlPlugin::NormalVersion:
iconName = "vcs-normal";
break;
case KVersionControlPlugin::UpdateRequiredVersion:
iconName = "vcs-update-required";
break;
case KVersionControlPlugin::LocallyModifiedVersion:
iconName = "vcs-locally-modified";
break;
case KVersionControlPlugin::LocallyModifiedUnstagedVersion:
iconName = "vcs-locally-modified-unstaged";
break;
case KVersionControlPlugin::AddedVersion:
iconName = "vcs-added";
break;
case KVersionControlPlugin::RemovedVersion:
iconName = "vcs-removed";
break;
case KVersionControlPlugin::ConflictingVersion:
iconName = "vcs-conflicting";
break;
case KVersionControlPlugin::UnversionedVersion:
break;
default:
Q_ASSERT(false);
break;
}
m_cachedEmblems[i] = KIcon(iconName).pixmap(emblemSize);
}
}
return m_cachedEmblems[state];
}

Some files were not shown because too many files have changed in this diff Show more