diff options
Diffstat (limited to 'src')
130 files changed, 9612 insertions, 10422 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bc880bb67..314c6cb28 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/config-X11.h.cmake b/src/config-X11.h.cmake new file mode 100644 index 000000000..d15b98539 --- /dev/null +++ b/src/config-X11.h.cmake @@ -0,0 +1 @@ +#cmakedefine HAVE_XRENDER 1 diff --git a/src/dolphincontextmenu.cpp b/src/dolphincontextmenu.cpp index 7aac7707b..2eb4e92d0 100644 --- a/src/dolphincontextmenu.cpp +++ b/src/dolphincontextmenu.cpp @@ -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()); diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 0f075a11c..a379f31b0 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -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); diff --git a/src/dolphinpart.cpp b/src/dolphinpart.cpp index 299043595..736ec6f4f 100644 --- a/src/dolphinpart.cpp +++ b/src/dolphinpart.cpp @@ -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(); } diff --git a/src/dolphinpart.desktop b/src/dolphinpart.desktop index 511f7f900..13dfacf47 100644 --- a/src/dolphinpart.desktop +++ b/src/dolphinpart.desktop @@ -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]=Тафсилотлар diff --git a/src/dolphinui.rc b/src/dolphinui.rc index 82803cc1b..68e03752f 100644 --- a/src/dolphinui.rc +++ b/src/dolphinui.rc @@ -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"/> diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index 67ea84d1b..8e00b0afc 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -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) -{ - 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) +void DolphinViewContainer::saveViewState() { - 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" diff --git a/src/dolphinviewcontainer.h b/src/dolphinviewcontainer.h index 37b06b7d6..7aea304b8 100644 --- a/src/dolphinviewcontainer.h +++ b/src/dolphinviewcontainer.h @@ -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; diff --git a/src/kitemviews/kfileitemlistview.cpp b/src/kitemviews/kfileitemlistview.cpp new file mode 100644 index 000000000..a7217d30c --- /dev/null +++ b/src/kitemviews/kfileitemlistview.cpp @@ -0,0 +1,423 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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" diff --git a/src/kitemviews/kfileitemlistview.h b/src/kitemviews/kfileitemlistview.h new file mode 100644 index 000000000..acd03ed22 --- /dev/null +++ b/src/kitemviews/kfileitemlistview.h @@ -0,0 +1,97 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 + + diff --git a/src/kitemviews/kfileitemlistwidget.cpp b/src/kitemviews/kfileitemlistwidget.cpp new file mode 100644 index 000000000..4abc4d115 --- /dev/null +++ b/src/kitemviews/kfileitemlistwidget.cpp @@ -0,0 +1,728 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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" diff --git a/src/kitemviews/kfileitemlistwidget.h b/src/kitemviews/kfileitemlistwidget.h new file mode 100644 index 000000000..3ce953106 --- /dev/null +++ b/src/kitemviews/kfileitemlistwidget.h @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 + + diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp new file mode 100644 index 000000000..b191abab6 --- /dev/null +++ b/src/kitemviews/kfileitemmodel.cpp @@ -0,0 +1,868 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 <[email protected]> + // Copyright (C) 2006 by Dominic Battre <[email protected]> + // Copyright (C) 2006 by Martin Pool <[email protected]> + + 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" diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h new file mode 100644 index 000000000..e6c89d744 --- /dev/null +++ b/src/kitemviews/kfileitemmodel.h @@ -0,0 +1,192 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 + + diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp new file mode 100644 index 000000000..325d08e18 --- /dev/null +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -0,0 +1,765 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 <[email protected]> + + 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" diff --git a/src/kitemviews/kfileitemmodelrolesupdater.h b/src/kitemviews/kfileitemmodelrolesupdater.h new file mode 100644 index 000000000..4931f9d64 --- /dev/null +++ b/src/kitemviews/kfileitemmodelrolesupdater.h @@ -0,0 +1,177 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 + + diff --git a/src/kitemviews/kitemlistcontainer.cpp b/src/kitemviews/kitemlistcontainer.cpp new file mode 100644 index 000000000..09fb505d6 --- /dev/null +++ b/src/kitemviews/kitemlistcontainer.cpp @@ -0,0 +1,191 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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" diff --git a/src/kitemviews/kitemlistcontainer.h b/src/kitemviews/kitemlistcontainer.h new file mode 100644 index 000000000..83044c4f8 --- /dev/null +++ b/src/kitemviews/kitemlistcontainer.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 + + diff --git a/src/kitemviews/kitemlistcontroller.cpp b/src/kitemviews/kitemlistcontroller.cpp new file mode 100644 index 000000000..b709e5606 --- /dev/null +++ b/src/kitemviews/kitemlistcontroller.cpp @@ -0,0 +1,281 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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" diff --git a/src/kitemviews/kitemlistcontroller.h b/src/kitemviews/kitemlistcontroller.h new file mode 100644 index 000000000..4407e3445 --- /dev/null +++ b/src/kitemviews/kitemlistcontroller.h @@ -0,0 +1,118 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 + + diff --git a/src/kitemviews/kitemlistgroupheader.cpp b/src/kitemviews/kitemlistgroupheader.cpp new file mode 100644 index 000000000..4db253293 --- /dev/null +++ b/src/kitemviews/kitemlistgroupheader.cpp @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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" diff --git a/src/settings/viewmodes/columnviewsettingspage.h b/src/kitemviews/kitemlistgroupheader.h index 2635e71f8..135fd5e5f 100644 --- a/src/settings/viewmodes/columnviewsettingspage.h +++ b/src/kitemviews/kitemlistgroupheader.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2006 by Peter Penz <[email protected]> * + * Copyright (C) 2011 by Peter Penz <[email protected]> * * * * 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(); - - /** - * Applies the settings for the details view. - * The settings are persisted automatically when - * closing Dolphin. - */ - virtual void applySettings(); - - /** Restores the settings to default values. */ - virtual void restoreDefaults(); - -private: - void loadSettings(); + KItemListGroupHeader(QGraphicsWidget* parent = 0); + virtual ~KItemListGroupHeader(); -private: - enum - { - BaseTextWidth = 200, - TextInc = 50 - }; + void setIndex(int index); + int index() const; - 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 + + diff --git a/src/kitemviews/kitemlistselectionmanager.cpp b/src/kitemviews/kitemlistselectionmanager.cpp new file mode 100644 index 000000000..6fe9ed818 --- /dev/null +++ b/src/kitemviews/kitemlistselectionmanager.cpp @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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" diff --git a/src/kitemviews/kitemlistselectionmanager.h b/src/kitemviews/kitemlistselectionmanager.h new file mode 100644 index 000000000..5c8e84614 --- /dev/null +++ b/src/kitemviews/kitemlistselectionmanager.h @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 diff --git a/src/kitemviews/kitemlistsizehintresolver.cpp b/src/kitemviews/kitemlistsizehintresolver.cpp new file mode 100644 index 000000000..00eb79bdd --- /dev/null +++ b/src/kitemviews/kitemlistsizehintresolver.cpp @@ -0,0 +1,86 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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(); + } +} diff --git a/src/kitemviews/kitemlistsizehintresolver_p.h b/src/kitemviews/kitemlistsizehintresolver_p.h new file mode 100644 index 000000000..f4f65eccb --- /dev/null +++ b/src/kitemviews/kitemlistsizehintresolver_p.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 diff --git a/src/tests/dolphinviewtest_details.cpp b/src/kitemviews/kitemliststyleoption.cpp index 0034afd90..261dfc07b 100644 --- a/src/tests/dolphinviewtest_details.cpp +++ b/src/kitemviews/kitemliststyleoption.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Frank Reininghaus ([email protected]) * + * Copyright (C) 2011 by Peter Penz <[email protected]> * * * * 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: - - 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) +KItemListStyleOption::KItemListStyleOption(const KItemListStyleOption& other) : + QStyleOption(other) +{ + margin = other.margin; + iconSize = other.iconSize; + font = other.font; +} -#include "dolphinviewtest_details.moc" +KItemListStyleOption::~KItemListStyleOption() +{ +} diff --git a/src/tests/dolphinviewtest_columns.cpp b/src/kitemviews/kitemliststyleoption.h index 40b88a223..d181204d7 100644 --- a/src/tests/dolphinviewtest_columns.cpp +++ b/src/kitemviews/kitemliststyleoption.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Frank Reininghaus ([email protected]) * + * Copyright (C) 2011 by Peter Penz <[email protected]> * * * * 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 -{ - Q_OBJECT +#include <QFont> +#include <QStyleOption> +class LIBDOLPHINPRIVATE_EXPORT KItemListStyleOption : public QStyleOption +{ 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" diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp new file mode 100644 index 000000000..a0897420c --- /dev/null +++ b/src/kitemviews/kitemlistview.cpp @@ -0,0 +1,1124 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 "kitemlistview.h" + +#include "kitemlistcontroller.h" +#include "kitemlistgroupheader.h" +#include "kitemlistselectionmanager.h" +#include "kitemlistsizehintresolver_p.h" +#include "kitemlistviewlayouter_p.h" +#include "kitemlistviewanimation_p.h" +#include "kitemlistwidget.h" + +#include <KDebug> + +#include <QGraphicsSceneMouseEvent> +#include <QPropertyAnimation> +#include <QStyle> +#include <QTimer> + +KItemListView::KItemListView(QGraphicsWidget* parent) : + QGraphicsWidget(parent), + m_grouped(false), + m_activeTransactions(0), + m_itemSize(), + m_controller(0), + m_model(0), + m_visibleRoles(), + m_visibleRolesSizes(), + m_widgetCreator(0), + m_groupHeaderCreator(0), + m_styleOption(), + m_visibleItems(), + m_visibleGroups(), + m_sizeHintResolver(0), + m_layouter(0), + m_animation(0), + m_layoutTimer(0), + m_oldOffset(0), + m_oldMaximumOffset(0) +{ + setAcceptHoverEvents(true); + + m_sizeHintResolver = new KItemListSizeHintResolver(this); + + m_layouter = new KItemListViewLayouter(this); + m_layouter->setSizeHintResolver(m_sizeHintResolver); + + m_animation = new KItemListViewAnimation(this); + connect(m_animation, SIGNAL(finished(QGraphicsWidget*, KItemListViewAnimation::AnimationType)), + this, SLOT(slotAnimationFinished(QGraphicsWidget*, KItemListViewAnimation::AnimationType))); + + m_layoutTimer = new QTimer(this); + m_layoutTimer->setInterval(300); + m_layoutTimer->setSingleShot(true); + connect(m_layoutTimer, SIGNAL(timeout()), this, SLOT(slotLayoutTimerFinished())); +} + +KItemListView::~KItemListView() +{ + delete m_sizeHintResolver; + m_sizeHintResolver = 0; +} + +void KItemListView::setScrollOrientation(Qt::Orientation orientation) +{ + const Qt::Orientation previousOrientation = m_layouter->scrollOrientation(); + if (orientation == previousOrientation) { + return; + } + + m_layouter->setScrollOrientation(orientation); + m_animation->setScrollOrientation(orientation); + m_sizeHintResolver->clearCache(); + updateLayout(); + onScrollOrientationChanged(orientation, previousOrientation); +} + +Qt::Orientation KItemListView::scrollOrientation() const +{ + return m_layouter->scrollOrientation(); +} + +void KItemListView::setItemSize(const QSizeF& itemSize) +{ + const QSizeF previousSize = m_itemSize; + if (itemSize == previousSize) { + return; + } + + m_itemSize = itemSize; + + if (!markVisibleRolesSizesAsDirty()) { + if (itemSize.width() < previousSize.width() || itemSize.height() < previousSize.height()) { + prepareLayoutForIncreasedItemCount(itemSize, ItemSize); + } else { + m_layouter->setItemSize(itemSize); + } + } + + m_sizeHintResolver->clearCache(); + updateLayout(); + onItemSizeChanged(itemSize, previousSize); +} + +QSizeF KItemListView::itemSize() const +{ + return m_itemSize; +} + +void KItemListView::setOffset(qreal offset) +{ + if (offset < 0) { + offset = 0; + } + + const qreal previousOffset = m_layouter->offset(); + if (offset == previousOffset) { + return; + } + + m_layouter->setOffset(offset); + m_animation->setOffset(offset); + if (!m_layoutTimer->isActive()) { + doLayout(NoAnimation, 0, 0); + update(); + } + onOffsetChanged(offset, previousOffset); +} + +qreal KItemListView::offset() const +{ + return m_layouter->offset(); +} + +qreal KItemListView::maximumOffset() const +{ + return m_layouter->maximumOffset(); +} + +void KItemListView::setVisibleRoles(const QHash<QByteArray, int>& roles) +{ + const QHash<QByteArray, int> previousRoles = m_visibleRoles; + m_visibleRoles = roles; + + QHashIterator<int, KItemListWidget*> it(m_visibleItems); + while (it.hasNext()) { + it.next(); + KItemListWidget* widget = it.value(); + widget->setVisibleRoles(roles); + widget->setVisibleRolesSizes(m_visibleRolesSizes); + } + + m_sizeHintResolver->clearCache(); + m_layouter->markAsDirty(); + onVisibleRolesChanged(roles, previousRoles); + + markVisibleRolesSizesAsDirty(); + updateLayout(); +} + +QHash<QByteArray, int> KItemListView::visibleRoles() const +{ + return m_visibleRoles; +} + +KItemListController* KItemListView::controller() const +{ + return m_controller; +} + +KItemModelBase* KItemListView::model() const +{ + return m_model; +} + +void KItemListView::setWidgetCreator(KItemListWidgetCreatorBase* widgetCreator) +{ + m_widgetCreator = widgetCreator; +} + +KItemListWidgetCreatorBase* KItemListView::widgetCreator() const +{ + return m_widgetCreator; +} + +void KItemListView::setGroupHeaderCreator(KItemListGroupHeaderCreatorBase* groupHeaderCreator) +{ + m_groupHeaderCreator = groupHeaderCreator; +} + +KItemListGroupHeaderCreatorBase* KItemListView::groupHeaderCreator() const +{ + return m_groupHeaderCreator; +} + +void KItemListView::setStyleOption(const KItemListStyleOption& option) +{ + const KItemListStyleOption previousOption = m_styleOption; + m_styleOption = option; + + QHashIterator<int, KItemListWidget*> it(m_visibleItems); + while (it.hasNext()) { + it.next(); + it.value()->setStyleOption(option); + } + + m_sizeHintResolver->clearCache(); + updateLayout(); + onStyleOptionChanged(option, previousOption); +} + +const KItemListStyleOption& KItemListView::styleOption() const +{ + return m_styleOption; +} + +void KItemListView::setGeometry(const QRectF& rect) +{ + QGraphicsWidget::setGeometry(rect); + if (!m_model) { + return; + } + + if (m_itemSize.isEmpty()) { + m_layouter->setItemSize(QSizeF()); + } + + if (m_model->count() > 0) { + prepareLayoutForIncreasedItemCount(rect.size(), LayouterSize); + } else { + m_layouter->setSize(rect.size()); + } + + m_layoutTimer->start(); +} + +int KItemListView::itemAt(const QPointF& pos) const +{ + if (!m_model) { + return -1; + } + + QHashIterator<int, KItemListWidget*> it(m_visibleItems); + while (it.hasNext()) { + it.next(); + + const KItemListWidget* widget = it.value(); + const QPointF mappedPos = widget->mapFromItem(this, pos); + if (widget->contains(mappedPos)) { + return it.key(); + } + } + + return -1; +} + +bool KItemListView::isAboveSelectionToggle(int index, const QPointF& pos) const +{ + Q_UNUSED(index); + Q_UNUSED(pos); + return false; +} + +bool KItemListView::isAboveExpansionToggle(int index, const QPointF& pos) const +{ + const KItemListWidget* widget = m_visibleItems.value(index); + if (widget) { + const QRectF expansionToggleRect = widget->expansionToggleRect(); + if (!expansionToggleRect.isEmpty()) { + const QPointF mappedPos = widget->mapFromItem(this, pos); + return expansionToggleRect.contains(mappedPos); + } + } + return false; +} + +int KItemListView::firstVisibleIndex() const +{ + return m_layouter->firstVisibleIndex(); +} + +int KItemListView::lastVisibleIndex() const +{ + return m_layouter->lastVisibleIndex(); +} + +QSizeF KItemListView::itemSizeHint(int index) const +{ + Q_UNUSED(index); + return itemSize(); +} + +QHash<QByteArray, QSizeF> KItemListView::visibleRoleSizes() const +{ + return QHash<QByteArray, QSizeF>(); +} + +void KItemListView::beginTransaction() +{ + ++m_activeTransactions; + if (m_activeTransactions == 1) { + onTransactionBegin(); + } +} + +void KItemListView::endTransaction() +{ + --m_activeTransactions; + if (m_activeTransactions < 0) { + m_activeTransactions = 0; + kWarning() << "Mismatch between beginTransaction()/endTransaction()"; + } + + if (m_activeTransactions == 0) { + onTransactionEnd(); + updateLayout(); + } +} + +bool KItemListView::isTransactionActive() const +{ + return m_activeTransactions > 0; +} + +void KItemListView::initializeItemListWidget(KItemListWidget* item) +{ + Q_UNUSED(item); +} + +void KItemListView::onControllerChanged(KItemListController* current, KItemListController* previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onModelChanged(KItemModelBase* current, KItemModelBase* previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onItemSizeChanged(const QSizeF& current, const QSizeF& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onOffsetChanged(qreal current, qreal previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onVisibleRolesChanged(const QHash<QByteArray, int>& current, const QHash<QByteArray, int>& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); +} + +void KItemListView::onTransactionBegin() +{ +} + +void KItemListView::onTransactionEnd() +{ +} + +bool KItemListView::event(QEvent* event) +{ + // Forward all events to the controller and handle them there + if (m_controller && m_controller->processEvent(event, transform())) { + event->accept(); + return true; + } + return QGraphicsWidget::event(event); +} + +void KItemListView::mousePressEvent(QGraphicsSceneMouseEvent* event) +{ + event->accept(); +} + +void KItemListView::hoverMoveEvent(QGraphicsSceneHoverEvent* event) +{ + if (!m_model) { + return; + } + + QHashIterator<int, KItemListWidget*> it(m_visibleItems); + while (it.hasNext()) { + it.next(); + + KItemListWidget* widget = it.value(); + KItemListStyleOption styleOption = widget->styleOption(); + const QPointF mappedPos = widget->mapFromItem(this, event->pos()); + + const bool hovered = widget->contains(mappedPos) && + !widget->expansionToggleRect().contains(mappedPos) && + !widget->selectionToggleRect().contains(mappedPos); + if (hovered) { + if (!(styleOption.state & QStyle::State_MouseOver)) { + styleOption.state |= QStyle::State_MouseOver; + widget->setStyleOption(styleOption); + } + } else if (styleOption.state & QStyle::State_MouseOver) { + styleOption.state &= ~QStyle::State_MouseOver; + widget->setStyleOption(styleOption); + } + } +} + +void KItemListView::hoverLeaveEvent(QGraphicsSceneHoverEvent* event) +{ + Q_UNUSED(event); + + if (!m_model) { + return; + } + + QHashIterator<int, KItemListWidget*> it(m_visibleItems); + while (it.hasNext()) { + it.next(); + + KItemListWidget* widget = it.value(); + KItemListStyleOption styleOption = widget->styleOption(); + if (styleOption.state & QStyle::State_MouseOver) { + styleOption.state &= ~QStyle::State_MouseOver; + widget->setStyleOption(styleOption); + } + } +} + +QList<KItemListWidget*> KItemListView::visibleItemListWidgets() const +{ + return m_visibleItems.values(); +} + +void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) +{ + markVisibleRolesSizesAsDirty(); + + const bool hasMultipleRanges = (itemRanges.count() > 1); + if (hasMultipleRanges) { + beginTransaction(); + } + + foreach (const KItemRange& range, itemRanges) { + const int index = range.index; + const int count = range.count; + if (index < 0 || count <= 0) { + kWarning() << "Invalid item range (index:" << index << ", count:" << count << ")"; + continue; + } + + m_sizeHintResolver->itemsInserted(index, count); + + // Determine which visible items must be moved + QList<int> itemsToMove; + QHashIterator<int, KItemListWidget*> it(m_visibleItems); + while (it.hasNext()) { + it.next(); + const int visibleItemIndex = it.key(); + if (visibleItemIndex >= index) { + itemsToMove.append(visibleItemIndex); + } + } + + // Update the indexes of all KItemListWidget instances that are located + // after the inserted items. It is important to adjust the indexes in the order + // from the highest index to the lowest index to prevent overlaps when setting the new index. + qSort(itemsToMove); + for (int i = itemsToMove.count() - 1; i >= 0; --i) { + KItemListWidget* widget = m_visibleItems.value(itemsToMove[i]); + Q_ASSERT(widget); + setWidgetIndex(widget, widget->index() + count); + } + + m_layouter->markAsDirty(); + if (m_model->count() == count && maximumOffset() > size().height()) { + kDebug() << "Scrollbar required, skipping layout"; + const int scrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent); + QSizeF layouterSize = m_layouter->size(); + if (scrollOrientation() == Qt::Vertical) { + layouterSize.rwidth() -= scrollBarExtent; + } else { + layouterSize.rheight() -= scrollBarExtent; + } + m_layouter->setSize(layouterSize); + } + + if (!hasMultipleRanges) { + doLayout(Animation, index, count); + update(); + } + + if (m_controller) { + KItemListSelectionManager* selectionManager = m_controller->selectionManager(); + const int current = selectionManager->currentItem(); + if (current < 0) { + selectionManager->setCurrentItem(0); + } else if (current >= index) { + selectionManager->setCurrentItem(current + count); + } + } + } + + if (hasMultipleRanges) { + endTransaction(); + } +} + +void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges) +{ + markVisibleRolesSizesAsDirty(); + + const bool hasMultipleRanges = (itemRanges.count() > 1); + if (hasMultipleRanges) { + beginTransaction(); + } + + for (int i = itemRanges.count() - 1; i >= 0; --i) { + const KItemRange& range = itemRanges.at(i); + const int index = range.index; + const int count = range.count; + if (index < 0 || count <= 0) { + kWarning() << "Invalid item range (index:" << index << ", count:" << count << ")"; + continue; + } + + m_sizeHintResolver->itemsRemoved(index, count); + + const int firstRemovedIndex = index; + const int lastRemovedIndex = index + count - 1; + const int lastIndex = m_model->count() + count - 1; + + // Remove all KItemListWidget instances that got deleted + for (int i = firstRemovedIndex; i <= lastRemovedIndex; ++i) { + KItemListWidget* widget = m_visibleItems.value(i); + if (!widget) { + continue; + } + + m_animation->stop(widget); + // Stopping the animation might lead to recycling the widget if + // it is invisible (see slotAnimationFinished()). + // Check again whether it is still visible: + if (!m_visibleItems.contains(i)) { + continue; + } + + if (m_model->count() == 0) { + // For performance reasons no animation is done when all items have + // been removed. + recycleWidget(widget); + } else { + // Animate the removing of the items. Special case: When removing an item there + // is no valid model index available anymore. For the + // remove-animation the item gets removed from m_visibleItems but the widget + // will stay alive until the animation has been finished and will + // be recycled (deleted) in KItemListView::slotAnimationFinished(). + m_visibleItems.remove(i); + widget->setIndex(-1); + m_animation->start(widget, KItemListViewAnimation::DeleteAnimation); + } + } + + // Update the indexes of all KItemListWidget instances that are located + // after the deleted items + for (int i = lastRemovedIndex + 1; i <= lastIndex; ++i) { + KItemListWidget* widget = m_visibleItems.value(i); + if (widget) { + const int newIndex = i - count; + setWidgetIndex(widget, newIndex); + } + } + + m_layouter->markAsDirty(); + if (!hasMultipleRanges) { + doLayout(Animation, index, -count); + update(); + } + + /*KItemListSelectionManager* selectionManager = m_controller->selectionManager(); + const int current = selectionManager->currentItem(); + if (count() <= 0) { + selectionManager->setCurrentItem(-1); + } else if (current >= index) { + selectionManager->setCurrentItem(current + count); + }*/ + } + + if (hasMultipleRanges) { + endTransaction(); + } +} + +void KItemListView::slotItemsChanged(const KItemRangeList& itemRanges, + const QSet<QByteArray>& roles) +{ + foreach (const KItemRange& itemRange, itemRanges) { + const int index = itemRange.index; + const int count = itemRange.count; + + m_sizeHintResolver->itemsChanged(index, count, roles); + + const int lastIndex = index + count - 1; + for (int i = index; i <= lastIndex; ++i) { + KItemListWidget* widget = m_visibleItems.value(i); + if (widget) { + widget->setData(m_model->data(i), roles); + } + } + } +} + +void KItemListView::slotAnimationFinished(QGraphicsWidget* widget, + KItemListViewAnimation::AnimationType type) +{ + KItemListWidget* itemListWidget = qobject_cast<KItemListWidget*>(widget); + Q_ASSERT(itemListWidget); + + switch (type) { + case KItemListViewAnimation::DeleteAnimation: { + // As we recycle the widget in this case it is important to assure that no + // other animation has been started. This is a convention in KItemListView and + // not a requirement defined by KItemListViewAnimation. + Q_ASSERT(!m_animation->isStarted(itemListWidget)); + + // All KItemListWidgets that are animated by the DeleteAnimation are not maintained + // by m_visibleWidgets and must be deleted manually after the animation has + // been finished. + KItemListGroupHeader* header = m_visibleGroups.value(itemListWidget); + if (header) { + m_groupHeaderCreator->recycle(header); + m_visibleGroups.remove(itemListWidget); + } + m_widgetCreator->recycle(itemListWidget); + break; + } + + case KItemListViewAnimation::CreateAnimation: + case KItemListViewAnimation::MovingAnimation: + case KItemListViewAnimation::ResizeAnimation: { + const int index = itemListWidget->index(); + const bool invisible = (index < m_layouter->firstVisibleIndex()) || + (index > m_layouter->lastVisibleIndex()); + if (invisible && !m_animation->isStarted(itemListWidget)) { + recycleWidget(itemListWidget); + } + break; + } + + default: break; + } +} + +void KItemListView::slotLayoutTimerFinished() +{ + m_layouter->setSize(geometry().size()); + doLayout(Animation, 0, 0); +} + +void KItemListView::setController(KItemListController* controller) +{ + if (m_controller != controller) { + KItemListController* previous = m_controller; + m_controller = controller; + onControllerChanged(controller, previous); + } +} + +void KItemListView::setModel(KItemModelBase* model) +{ + if (m_model == model) { + return; + } + + KItemModelBase* previous = m_model; + + if (m_model) { + disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>))); + disconnect(m_model, SIGNAL(itemsInserted(KItemRangeList)), + this, SLOT(slotItemsInserted(KItemRangeList))); + disconnect(m_model, SIGNAL(itemsRemoved(KItemRangeList)), + this, SLOT(slotItemsRemoved(KItemRangeList))); + } + + m_model = model; + m_layouter->setModel(model); + m_grouped = !model->groupRole().isEmpty(); + + if (m_model) { + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet<QByteArray>)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet<QByteArray>))); + connect(m_model, SIGNAL(itemsInserted(KItemRangeList)), + this, SLOT(slotItemsInserted(KItemRangeList))); + connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)), + this, SLOT(slotItemsRemoved(KItemRangeList))); + } + + onModelChanged(model, previous); +} + +void KItemListView::updateLayout() +{ + doLayout(Animation, 0, 0); + update(); +} + +void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int changedCount) +{ + if (m_layoutTimer->isActive()) { + kDebug() << "Stopping layout timer, synchronous layout requested"; + m_layoutTimer->stop(); + } + + if (m_model->count() < 0 || m_activeTransactions > 0) { + return; + } + + applyDynamicItemSize(); + + const int firstVisibleIndex = m_layouter->firstVisibleIndex(); + const int lastVisibleIndex = m_layouter->lastVisibleIndex(); + if (firstVisibleIndex < 0) { + emitOffsetChanges(); + return; + } + + // Do a sanity check of the offset-property: When properties of the itemlist-view have been changed + // it might be possible that the maximum offset got changed too. Assure that the full visible range + // is still shown if the maximum offset got decreased. + const qreal visibleOffsetRange = (scrollOrientation() == Qt::Horizontal) ? size().width() : size().height(); + const qreal maxOffsetToShowFullRange = maximumOffset() - visibleOffsetRange; + if (offset() > maxOffsetToShowFullRange) { + m_layouter->setOffset(qMax(qreal(0), maxOffsetToShowFullRange)); + } + + // Determine all items that are completely invisible and might be + // reused for items that just got (at least partly) visible. + // Items that do e.g. an animated moving of their position are not + // marked as invisible: This assures that a scrolling inside the view + // can be done without breaking an animation. + QList<int> reusableItems; + QHashIterator<int, KItemListWidget*> it(m_visibleItems); + while (it.hasNext()) { + it.next(); + KItemListWidget* widget = it.value(); + const int index = widget->index(); + const bool invisible = (index < firstVisibleIndex) || (index > lastVisibleIndex); + if (invisible && !m_animation->isStarted(widget)) { + widget->setVisible(false); + reusableItems.append(index); + } + } + + // Assure that for each visible item a KItemListWidget is available. KItemListWidget + // instances from invisible items are reused. If no reusable items are + // found then new KItemListWidget instances get created. + const bool animate = (hint == Animation); + for (int i = firstVisibleIndex; i <= lastVisibleIndex; ++i) { + bool applyNewPos = true; + bool wasHidden = false; + + const QRectF itemBounds = m_layouter->itemBoundingRect(i); + const QPointF newPos = itemBounds.topLeft(); + KItemListWidget* widget = m_visibleItems.value(i); + if (!widget) { + wasHidden = true; + if (!reusableItems.isEmpty()) { + // Reuse a KItemListWidget instance from an invisible item + const int oldIndex = reusableItems.takeLast(); + widget = m_visibleItems.value(oldIndex); + setWidgetIndex(widget, i); + } else { + // No reusable KItemListWidget instance is available, create a new one + widget = createWidget(i); + } + widget->resize(itemBounds.size()); + + if (animate && changedCount < 0) { + // Items have been deleted, move the created item to the + // imaginary old position. + const QRectF itemBoundingRect = m_layouter->itemBoundingRect(i - changedCount); + if (itemBoundingRect.isEmpty()) { + const QPointF invisibleOldPos = (scrollOrientation() == Qt::Vertical) + ? QPointF(0, size().height()) : QPointF(size().width(), 0); + widget->setPos(invisibleOldPos); + } else { + widget->setPos(itemBoundingRect.topLeft()); + } + applyNewPos = false; + } + } else if (m_animation->isStarted(widget, KItemListViewAnimation::MovingAnimation)) { + applyNewPos = false; + } + + if (animate) { + const bool itemsRemoved = (changedCount < 0); + const bool itemsInserted = (changedCount > 0); + + if (itemsRemoved && (i >= changedIndex + changedCount + 1)) { + // The item is located after the removed items. Animate the moving of the position. + m_animation->start(widget, KItemListViewAnimation::MovingAnimation, newPos); + applyNewPos = false; + } else if (itemsInserted && i >= changedIndex) { + // The item is located after the first inserted item + if (i <= changedIndex + changedCount - 1) { + // The item is an inserted item. Animate the appearing of the item. + // For performance reasons no animation is done when changedCount is equal + // to all available items. + if (changedCount < m_model->count()) { + m_animation->start(widget, KItemListViewAnimation::CreateAnimation); + } + } else if (!m_animation->isStarted(widget, KItemListViewAnimation::CreateAnimation)) { + // The item was already there before, so animate the moving of the position. + // No moving animation is done if the item is animated by a create animation: This + // prevents a "move animation mess" when inserting several ranges in parallel. + m_animation->start(widget, KItemListViewAnimation::MovingAnimation, newPos); + applyNewPos = false; + } + } else if (!itemsRemoved && !itemsInserted && !wasHidden) { + // The size of the view might have been changed. Animate the moving of the position. + m_animation->start(widget, KItemListViewAnimation::MovingAnimation, newPos); + applyNewPos = false; + } + } + + if (applyNewPos) { + widget->setPos(newPos); + } + + Q_ASSERT(widget->index() == i); + widget->setVisible(true); + + if (widget->size() != itemBounds.size()) { + m_animation->start(widget, KItemListViewAnimation::ResizeAnimation, itemBounds.size()); + } + } + + // Delete invisible KItemListWidget instances that have not been reused + foreach (int index, reusableItems) { + recycleWidget(m_visibleItems.value(index)); + } + + emitOffsetChanges(); +} + +void KItemListView::emitOffsetChanges() +{ + const int newOffset = m_layouter->offset(); + if (m_oldOffset != newOffset) { + emit offsetChanged(newOffset, m_oldOffset); + m_oldOffset = newOffset; + } + + const int newMaximumOffset = m_layouter->maximumOffset(); + if (m_oldMaximumOffset != newMaximumOffset) { + emit maximumOffsetChanged(newMaximumOffset, m_oldMaximumOffset); + m_oldMaximumOffset = newMaximumOffset; + } +} + +KItemListWidget* KItemListView::createWidget(int index) +{ + KItemListWidget* widget = m_widgetCreator->create(this); + widget->setVisibleRoles(m_visibleRoles); + widget->setVisibleRolesSizes(m_visibleRolesSizes); + widget->setStyleOption(m_styleOption); + widget->setIndex(index); + widget->setData(m_model->data(index)); + m_visibleItems.insert(index, widget); + + if (m_grouped) { + if (m_layouter->isFirstGroupItem(index)) { + KItemListGroupHeader* header = m_groupHeaderCreator->create(widget); + header->setPos(0, -50); + header->resize(50, 50); + m_visibleGroups.insert(widget, header); + } + } + + initializeItemListWidget(widget); + return widget; +} + +void KItemListView::recycleWidget(KItemListWidget* widget) +{ + if (m_grouped) { + KItemListGroupHeader* header = m_visibleGroups.value(widget); + if (header) { + m_groupHeaderCreator->recycle(header); + m_visibleGroups.remove(widget); + } + } + + m_visibleItems.remove(widget->index()); + m_widgetCreator->recycle(widget); +} + +void KItemListView::setWidgetIndex(KItemListWidget* widget, int index) +{ + if (m_grouped) { + bool createHeader = m_layouter->isFirstGroupItem(index); + KItemListGroupHeader* header = m_visibleGroups.value(widget); + if (header) { + if (createHeader) { + createHeader = false; + } else { + m_groupHeaderCreator->recycle(header); + m_visibleGroups.remove(widget); + } + } + + if (createHeader) { + KItemListGroupHeader* header = m_groupHeaderCreator->create(widget); + header->setPos(0, -50); + header->resize(50, 50); + m_visibleGroups.insert(widget, header); + } + } + + const int oldIndex = widget->index(); + m_visibleItems.remove(oldIndex); + widget->setVisibleRoles(m_visibleRoles); + widget->setVisibleRolesSizes(m_visibleRolesSizes); + widget->setStyleOption(m_styleOption); + widget->setIndex(index); + widget->setData(m_model->data(index)); + m_visibleItems.insert(index, widget); + + initializeItemListWidget(widget); +} + +void KItemListView::prepareLayoutForIncreasedItemCount(const QSizeF& size, SizeType sizeType) +{ + // Calculate the first visible index and last visible index for the current size + const int currentFirst = m_layouter->firstVisibleIndex(); + const int currentLast = m_layouter->lastVisibleIndex(); + + const QSizeF currentSize = (sizeType == LayouterSize) ? m_layouter->size() : m_layouter->itemSize(); + + // Calculate the first visible index and last visible index for the new size + setLayouterSize(size, sizeType); + const int newFirst = m_layouter->firstVisibleIndex(); + const int newLast = m_layouter->lastVisibleIndex(); + + if ((currentFirst != newFirst) || (currentLast != newLast)) { + // At least one index has been changed. Assure that widgets for all possible + // visible items get created so that a move-animation can be started later. + const int maxVisibleItems = m_layouter->maximumVisibleItems(); + int minFirst = qMin(newFirst, currentFirst); + const int maxLast = qMax(newLast, currentLast); + + if (maxLast - minFirst + 1 < maxVisibleItems) { + // Increasing the size might result in a smaller KItemListView::offset(). + // Decrease the first visible index in a way that at least the maximum + // visible items are shown. + minFirst = qMax(0, maxLast - maxVisibleItems + 1); + } + + if (maxLast - minFirst > maxVisibleItems + maxVisibleItems / 2) { + // The creating of widgets is quite expensive. Assure that never more + // than 50 % of the maximum visible items get created for the animations. + return; + } + + setLayouterSize(currentSize, sizeType); + for (int i = minFirst; i <= maxLast; ++i) { + if (!m_visibleItems.contains(i)) { + KItemListWidget* widget = createWidget(i); + const QPointF pos = m_layouter->itemBoundingRect(i).topLeft(); + widget->setPos(pos); + } + } + setLayouterSize(size, sizeType); + } +} + +void KItemListView::setLayouterSize(const QSizeF& size, SizeType sizeType) +{ + switch (sizeType) { + case LayouterSize: m_layouter->setSize(size); break; + case ItemSize: m_layouter->setItemSize(size); break; + default: break; + } +} + +bool KItemListView::markVisibleRolesSizesAsDirty() +{ + const bool dirty = m_itemSize.isEmpty(); + if (dirty) { + m_visibleRolesSizes.clear(); + m_layouter->setItemSize(QSizeF()); + } + return dirty; +} + +void KItemListView::applyDynamicItemSize() +{ + if (!m_itemSize.isEmpty()) { + return; + } + + if (m_visibleRolesSizes.isEmpty()) { + m_visibleRolesSizes = visibleRoleSizes(); + foreach (KItemListWidget* widget, visibleItemListWidgets()) { + widget->setVisibleRolesSizes(m_visibleRolesSizes); + } + } + + if (m_layouter->itemSize().isEmpty()) { + qreal requiredWidth = 0; + qreal requiredHeight = 0; + + QHashIterator<QByteArray, QSizeF> it(m_visibleRolesSizes); + while (it.hasNext()) { + it.next(); + const QSizeF& visibleRoleSize = it.value(); + requiredWidth += visibleRoleSize.width(); + requiredHeight += visibleRoleSize.height(); + } + + QSizeF dynamicItemSize = m_itemSize; + if (dynamicItemSize.width() <= 0) { + dynamicItemSize.setWidth(qMax(requiredWidth, size().width())); + } + if (dynamicItemSize.height() <= 0) { + dynamicItemSize.setHeight(qMax(requiredHeight, size().height())); + } + + m_layouter->setItemSize(dynamicItemSize); + } +} + +KItemListCreatorBase::~KItemListCreatorBase() +{ + qDeleteAll(m_recycleableWidgets); + qDeleteAll(m_createdWidgets); +} + +void KItemListCreatorBase::addCreatedWidget(QGraphicsWidget* widget) +{ + m_createdWidgets.insert(widget); +} + +void KItemListCreatorBase::pushRecycleableWidget(QGraphicsWidget* widget) +{ + Q_ASSERT(m_createdWidgets.contains(widget)); + m_createdWidgets.remove(widget); + + if (m_recycleableWidgets.count() < 100) { + m_recycleableWidgets.append(widget); + widget->setVisible(false); + } else { + delete widget; + } +} + +QGraphicsWidget* KItemListCreatorBase::popRecycleableWidget() +{ + if (m_recycleableWidgets.isEmpty()) { + return 0; + } + + QGraphicsWidget* widget = m_recycleableWidgets.takeLast(); + m_createdWidgets.insert(widget); + return widget; +} + +KItemListWidgetCreatorBase::~KItemListWidgetCreatorBase() +{ +} + +void KItemListWidgetCreatorBase::recycle(KItemListWidget* widget) +{ + widget->setOpacity(1.0); + pushRecycleableWidget(widget); +} + +KItemListGroupHeaderCreatorBase::~KItemListGroupHeaderCreatorBase() +{ +} + +void KItemListGroupHeaderCreatorBase::recycle(KItemListGroupHeader* header) +{ + header->setOpacity(1.0); + pushRecycleableWidget(header); +} + +#include "kitemlistview.moc" diff --git a/src/kitemviews/kitemlistview.h b/src/kitemviews/kitemlistview.h new file mode 100644 index 000000000..829736a09 --- /dev/null +++ b/src/kitemviews/kitemlistview.h @@ -0,0 +1,360 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 diff --git a/src/kitemviews/kitemlistviewanimation.cpp b/src/kitemviews/kitemlistviewanimation.cpp new file mode 100644 index 000000000..449d557f9 --- /dev/null +++ b/src/kitemviews/kitemlistviewanimation.cpp @@ -0,0 +1,241 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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" diff --git a/src/kitemviews/kitemlistviewanimation_p.h b/src/kitemviews/kitemlistviewanimation_p.h new file mode 100644 index 000000000..0bf54d296 --- /dev/null +++ b/src/kitemviews/kitemlistviewanimation_p.h @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 + + diff --git a/src/kitemviews/kitemlistviewlayouter.cpp b/src/kitemviews/kitemlistviewlayouter.cpp new file mode 100644 index 000000000..7d420e093 --- /dev/null +++ b/src/kitemviews/kitemlistviewlayouter.cpp @@ -0,0 +1,474 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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" diff --git a/src/kitemviews/kitemlistviewlayouter_p.h b/src/kitemviews/kitemlistviewlayouter_p.h new file mode 100644 index 000000000..775e9ff85 --- /dev/null +++ b/src/kitemviews/kitemlistviewlayouter_p.h @@ -0,0 +1,126 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 + + diff --git a/src/kitemviews/kitemlistwidget.cpp b/src/kitemviews/kitemlistwidget.cpp new file mode 100644 index 000000000..3f08d9f7a --- /dev/null +++ b/src/kitemviews/kitemlistwidget.cpp @@ -0,0 +1,260 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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" diff --git a/src/kitemviews/kitemlistwidget.h b/src/kitemviews/kitemlistwidget.h new file mode 100644 index 000000000..eb2ebf455 --- /dev/null +++ b/src/kitemviews/kitemlistwidget.h @@ -0,0 +1,134 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 + + diff --git a/src/kitemviews/kitemmodelbase.cpp b/src/kitemviews/kitemmodelbase.cpp new file mode 100644 index 000000000..63e771ef7 --- /dev/null +++ b/src/kitemviews/kitemmodelbase.cpp @@ -0,0 +1,113 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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" diff --git a/src/kitemviews/kitemmodelbase.h b/src/kitemviews/kitemmodelbase.h new file mode 100644 index 000000000..4eb96c8fd --- /dev/null +++ b/src/kitemviews/kitemmodelbase.h @@ -0,0 +1,145 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 + + diff --git a/src/kitemviews/kpixmapmodifier.cpp b/src/kitemviews/kpixmapmodifier.cpp new file mode 100644 index 000000000..e210f0bf5 --- /dev/null +++ b/src/kitemviews/kpixmapmodifier.cpp @@ -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 <[email protected]> + Copyright © 2006-2007, 2008 Fredrik Höglund <[email protected]> + + The stack blur algorithm was invented by Mario Klingemann <[email protected]> + + 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; +} + diff --git a/src/tests/dolphinviewtest_icons.cpp b/src/kitemviews/kpixmapmodifier_p.h index 5f928f403..bca5e442a 100644 --- a/src/tests/dolphinviewtest_icons.cpp +++ b/src/kitemviews/kpixmapmodifier_p.h @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010 by Frank Reininghaus ([email protected]) * + * Copyright (C) 2011 by Peter Penz <[email protected]> * * * * 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 -{ - Q_OBJECT +class QPixmap; +class QSize; +class LIBDOLPHINPRIVATE_EXPORT KPixmapModifier +{ 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" diff --git a/src/main.cpp b/src/main.cpp index 3f26bbb57..f8d50e9e1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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")); diff --git a/src/panels/folders/dolphin_folderspanelsettings.kcfg b/src/panels/folders/dolphin_folderspanelsettings.kcfg index 716be829c..8b8ca66f8 100644 --- a/src/panels/folders/dolphin_folderspanelsettings.kcfg +++ b/src/panels/folders/dolphin_folderspanelsettings.kcfg @@ -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"> diff --git a/src/panels/folders/folderspanel.cpp b/src/panels/folders/folderspanel.cpp index ccdf13dec..fe0f0b134 100644 --- a/src/panels/folders/folderspanel.cpp +++ b/src/panels/folders/folderspanel.cpp @@ -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" diff --git a/src/panels/folders/folderspanel.h b/src/panels/folders/folderspanel.h index 3031166e9..727fc7c71 100644 --- a/src/panels/folders/folderspanel.h +++ b/src/panels/folders/folderspanel.h @@ -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; }; diff --git a/src/panels/folders/paneltreeview.cpp b/src/panels/folders/paneltreeview.cpp index f6aa73aea..26a543d56 100644 --- a/src/panels/folders/paneltreeview.cpp +++ b/src/panels/folders/paneltreeview.cpp @@ -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) diff --git a/src/panels/folders/treeviewcontextmenu.cpp b/src/panels/folders/treeviewcontextmenu.cpp index b66d445cc..5db3e2c2a 100644 --- a/src/panels/folders/treeviewcontextmenu.cpp +++ b/src/panels/folders/treeviewcontextmenu.cpp @@ -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) diff --git a/src/panels/information/dolphin_informationpanelsettings.kcfg b/src/panels/information/dolphin_informationpanelsettings.kcfg index 4df3dedcd..53c756d24 100644 --- a/src/panels/information/dolphin_informationpanelsettings.kcfg +++ b/src/panels/information/dolphin_informationpanelsettings.kcfg @@ -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> diff --git a/src/panels/information/informationpanelcontent.cpp b/src/panels/information/informationpanelcontent.cpp index 2d90007bf..1084fa085 100644 --- a/src/panels/information/informationpanelcontent.cpp +++ b/src/panels/information/informationpanelcontent.cpp @@ -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", diff --git a/src/panels/places/placespanel.cpp b/src/panels/places/placespanel.cpp index c2af7a6d7..e70fdf574 100644 --- a/src/panels/places/placespanel.cpp +++ b/src/panels/places/placespanel.cpp @@ -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) diff --git a/src/settings/additionalinfodialog.cpp b/src/settings/additionalinfodialog.cpp index 2e7d33cb3..9b47e928f 100644 --- a/src/settings/additionalinfodialog.cpp +++ b/src/settings/additionalinfodialog.cpp @@ -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); } diff --git a/src/settings/additionalinfodialog.h b/src/settings/additionalinfodialog.h index eca2c86c6..6f38f617c 100644 --- a/src/settings/additionalinfodialog.h +++ b/src/settings/additionalinfodialog.h @@ -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; }; diff --git a/src/settings/applyviewpropsjob.cpp b/src/settings/applyviewpropsjob.cpp index 4010ab477..dd9788851 100644 --- a/src/settings/applyviewpropsjob.cpp +++ b/src/settings/applyviewpropsjob.cpp @@ -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()); diff --git a/src/settings/dolphin_columnmodesettings.kcfgc b/src/settings/dolphin_columnmodesettings.kcfgc deleted file mode 100644 index 55134335a..000000000 --- a/src/settings/dolphin_columnmodesettings.kcfgc +++ /dev/null @@ -1,4 +0,0 @@ -File=dolphin_columnmodesettings.kcfg -ClassName=ColumnModeSettings -Singleton=false -Mutators=true diff --git a/src/settings/dolphin_columnmodesettings.kcfg b/src/settings/dolphin_compactmodesettings.kcfg index 963d326cc..bb050d480 100644 --- a/src/settings/dolphin_columnmodesettings.kcfg +++ b/src/settings/dolphin_compactmodesettings.kcfg @@ -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> diff --git a/src/settings/dolphin_compactmodesettings.kcfgc b/src/settings/dolphin_compactmodesettings.kcfgc new file mode 100644 index 000000000..8341185fa --- /dev/null +++ b/src/settings/dolphin_compactmodesettings.kcfgc @@ -0,0 +1,4 @@ +File=dolphin_compactmodesettings.kcfg +ClassName=CompactModeSettings +Singleton=yes +Mutators=true diff --git a/src/settings/dolphin_detailsmodesettings.kcfg b/src/settings/dolphin_detailsmodesettings.kcfg index d58e03809..0fb1bf0b5 100644 --- a/src/settings/dolphin_detailsmodesettings.kcfg +++ b/src/settings/dolphin_detailsmodesettings.kcfg @@ -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> diff --git a/src/settings/dolphin_detailsmodesettings.kcfgc b/src/settings/dolphin_detailsmodesettings.kcfgc index 06da2e9e0..7acfa3c7d 100644 --- a/src/settings/dolphin_detailsmodesettings.kcfgc +++ b/src/settings/dolphin_detailsmodesettings.kcfgc @@ -1,4 +1,4 @@ File=dolphin_detailsmodesettings.kcfg ClassName=DetailsModeSettings -Singleton=false +Singleton=yes Mutators=true diff --git a/src/settings/dolphin_directoryviewpropertysettings.kcfg b/src/settings/dolphin_directoryviewpropertysettings.kcfg index 004152063..7e62267a9 100644 --- a/src/settings/dolphin_directoryviewpropertysettings.kcfg +++ b/src/settings/dolphin_directoryviewpropertysettings.kcfg @@ -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> diff --git a/src/settings/dolphin_iconsmodesettings.kcfg b/src/settings/dolphin_iconsmodesettings.kcfg index f50ab3ead..fb01a8c65 100644 --- a/src/settings/dolphin_iconsmodesettings.kcfg +++ b/src/settings/dolphin_iconsmodesettings.kcfg @@ -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> diff --git a/src/settings/dolphin_iconsmodesettings.kcfgc b/src/settings/dolphin_iconsmodesettings.kcfgc index 9987ce938..9ab145bc2 100644 --- a/src/settings/dolphin_iconsmodesettings.kcfgc +++ b/src/settings/dolphin_iconsmodesettings.kcfgc @@ -1,4 +1,4 @@ File=dolphin_iconsmodesettings.kcfg ClassName=IconsModeSettings -Singleton=false +Singleton=yes Mutators=true diff --git a/src/settings/dolphinsettings.cpp b/src/settings/dolphinsettings.cpp index 7f160b141..9fc0cea3d 100644 --- a/src/settings/dolphinsettings.cpp +++ b/src/settings/dolphinsettings.cpp @@ -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; } diff --git a/src/settings/dolphinsettings.h b/src/settings/dolphinsettings.h index 54141ad3e..88e1d2905 100644 --- a/src/settings/dolphinsettings.h +++ b/src/settings/dolphinsettings.h @@ -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; diff --git a/src/settings/kcm/kcmdolphinviewmodes.cpp b/src/settings/kcm/kcmdolphinviewmodes.cpp index b5ed44f63..4e49257ce 100644 --- a/src/settings/kcm/kcmdolphinviewmodes.cpp +++ b/src/settings/kcm/kcmdolphinviewmodes.cpp @@ -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); } diff --git a/src/settings/viewmodes/columnviewsettingspage.cpp b/src/settings/viewmodes/columnviewsettingspage.cpp deleted file mode 100644 index 888e35d4b..000000000 --- a/src/settings/viewmodes/columnviewsettingspage.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006 by Peter Penz <[email protected]> * - * * - * 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" diff --git a/src/settings/viewmodes/detailsviewsettingspage.cpp b/src/settings/viewmodes/detailsviewsettingspage.cpp index fbf26902f..b2338dc9b 100644 --- a/src/settings/viewmodes/detailsviewsettingspage.cpp +++ b/src/settings/viewmodes/detailsviewsettingspage.cpp @@ -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" diff --git a/src/settings/viewmodes/detailsviewsettingspage.h b/src/settings/viewmodes/detailsviewsettingspage.h index 73d911a32..9532241aa 100644 --- a/src/settings/viewmodes/detailsviewsettingspage.h +++ b/src/settings/viewmodes/detailsviewsettingspage.h @@ -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 diff --git a/src/settings/viewmodes/iconsviewsettingspage.cpp b/src/settings/viewmodes/iconsviewsettingspage.cpp index 91ca738a1..41438fb22 100644 --- a/src/settings/viewmodes/iconsviewsettingspage.cpp +++ b/src/settings/viewmodes/iconsviewsettingspage.cpp @@ -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(); - - const int arrangement = (m_arrangementBox->currentIndex() == 0) ? - QListView::LeftToRight : - QListView::TopToBottom; - settings->setArrangement(arrangement); - - 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; - } + IconsModeSettings::setUseSystemFont(m_fontRequester->mode() == DolphinFontRequester::SystemFont); + IconsModeSettings::setFontFamily(font.family()); + IconsModeSettings::setFontSize(font.pointSizeF()); + IconsModeSettings::setItalicFont(font.italic()); + IconsModeSettings::setFontWeight(font.weight()); - settings->setItemWidth(itemWidth); - settings->setItemHeight(itemHeight); + IconsModeSettings::setTextWidthIndex(m_textWidthBox->currentIndex()); - 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" diff --git a/src/settings/viewmodes/iconsviewsettingspage.h b/src/settings/viewmodes/iconsviewsettingspage.h index 3b60cdf7f..ad829f131 100644 --- a/src/settings/viewmodes/iconsviewsettingspage.h +++ b/src/settings/viewmodes/iconsviewsettingspage.h @@ -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 diff --git a/src/settings/viewmodes/viewsettingspage.cpp b/src/settings/viewmodes/viewsettingspage.cpp index 4530d866d..ac4ed9296 100644 --- a/src/settings/viewmodes/viewsettingspage.cpp +++ b/src/settings/viewmodes/viewsettingspage.cpp @@ -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); } diff --git a/src/settings/viewpropertiesdialog.cpp b/src/settings/viewpropertiesdialog.cpp index 2a35f7eb8..5f57d6eb4 100644 --- a/src/settings/viewpropertiesdialog.cpp +++ b/src/settings/viewpropertiesdialog.cpp @@ -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); } diff --git a/src/settings/viewpropertiesdialog.h b/src/settings/viewpropertiesdialog.h index d2170f331..694cffe88 100644 --- a/src/settings/viewpropertiesdialog.h +++ b/src/settings/viewpropertiesdialog.h @@ -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; diff --git a/src/statusbar/dolphinstatusbar.cpp b/src/statusbar/dolphinstatusbar.cpp index 2433d7364..b005d45c6 100644 --- a/src/statusbar/dolphinstatusbar.cpp +++ b/src/statusbar/dolphinstatusbar.cpp @@ -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) diff --git a/src/statusbar/dolphinstatusbar.h b/src/statusbar/dolphinstatusbar.h index 56244001b..7d56746f6 100644 --- a/src/statusbar/dolphinstatusbar.h +++ b/src/statusbar/dolphinstatusbar.h @@ -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; diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index e10f8d0c9..5038ee8d7 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -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}) diff --git a/src/tests/dolphindetailsviewtest.cpp b/src/tests/dolphindetailsviewtest.cpp deleted file mode 100644 index 31b4a57cd..000000000 --- a/src/tests/dolphindetailsviewtest.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2010 by Frank Reininghaus ([email protected]) * - * * - * 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" diff --git a/src/tests/dolphintreeviewtest.cpp b/src/tests/dolphintreeviewtest.cpp deleted file mode 100644 index 149570d1a..000000000 --- a/src/tests/dolphintreeviewtest.cpp +++ /dev/null @@ -1,413 +0,0 @@ -/***************************************************************************** - * Copyright (C) 2010-2011 by Frank Reininghaus ([email protected]) * - * * - * 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" diff --git a/src/tests/dolphinviewtest_allviewmodes.cpp b/src/tests/dolphinviewtest_allviewmodes.cpp deleted file mode 100644 index cac4c7102..000000000 --- a/src/tests/dolphinviewtest_allviewmodes.cpp +++ /dev/null @@ -1,596 +0,0 @@ -/**************************************************************************** - * Copyright (C) 2010-2011 by Frank Reininghaus ([email protected]) * - * * - * 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" diff --git a/src/tests/dolphinviewtest_allviewmodes.h b/src/tests/dolphinviewtest_allviewmodes.h deleted file mode 100644 index d062bb016..000000000 --- a/src/tests/dolphinviewtest_allviewmodes.h +++ /dev/null @@ -1,80 +0,0 @@ -/***************************************************************************** - * Copyright (C) 2010-2011 by Frank Reininghaus ([email protected]) * - * * - * 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 diff --git a/src/tests/kfileitemlistviewtest.cpp b/src/tests/kfileitemlistviewtest.cpp new file mode 100644 index 000000000..1b9e1b312 --- /dev/null +++ b/src/tests/kfileitemlistviewtest.cpp @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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" diff --git a/src/tests/kfileitemmodeltest.cpp b/src/tests/kfileitemmodeltest.cpp new file mode 100644 index 000000000..82fc3fbc0 --- /dev/null +++ b/src/tests/kfileitemmodeltest.cpp @@ -0,0 +1,210 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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" diff --git a/src/tests/testbase.cpp b/src/tests/testbase.cpp deleted file mode 100644 index de323aac2..000000000 --- a/src/tests/testbase.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/***************************************************************************** - * Copyright (C) 2010-2011 by Frank Reininghaus ([email protected]) * - * * - * 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; -} diff --git a/src/tests/testbase.h b/src/tests/testbase.h deleted file mode 100644 index 957d822c3..000000000 --- a/src/tests/testbase.h +++ /dev/null @@ -1,67 +0,0 @@ -/***************************************************************************** - * Copyright (C) 2010-2011 by Frank Reininghaus ([email protected]) * - * * - * 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 diff --git a/src/tests/testdir.cpp b/src/tests/testdir.cpp index 64d7f1aaa..5e961d2ab 100644 --- a/src/tests/testdir.cpp +++ b/src/tests/testdir.cpp @@ -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); } } diff --git a/src/tests/testdir.h b/src/tests/testdir.h index bff462690..bcaa034b5 100644 --- a/src/tests/testdir.h +++ b/src/tests/testdir.h @@ -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); }; diff --git a/src/views/additionalinfoaccessor.cpp b/src/views/additionalinfoaccessor.cpp index 426d7a2c4..465a2b3dd 100644 --- a/src/views/additionalinfoaccessor.cpp +++ b/src/views/additionalinfoaccessor.cpp @@ -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() diff --git a/src/views/additionalinfoaccessor.h b/src/views/additionalinfoaccessor.h index dbec097fd..d6dcc8e3f 100644 --- a/src/views/additionalinfoaccessor.h +++ b/src/views/additionalinfoaccessor.h @@ -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; - - DolphinView::Sorting sorting(KFileItemDelegate::Information info) const; + QString value(DolphinView::AdditionalInfo 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 diff --git a/src/views/dolphincategorydrawer.cpp b/src/views/dolphincategorydrawer.cpp deleted file mode 100644 index 1f39133ab..000000000 --- a/src/views/dolphincategorydrawer.cpp +++ /dev/null @@ -1,377 +0,0 @@ -/* - * This file is part of the KDE project - * Copyright (C) 2007 Rafael Fernández López <[email protected]> - * - * 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(); -} diff --git a/src/views/dolphincategorydrawer.h b/src/views/dolphincategorydrawer.h deleted file mode 100644 index 94c9e69ef..000000000 --- a/src/views/dolphincategorydrawer.h +++ /dev/null @@ -1,85 +0,0 @@ -/* This file is part of the KDE project - * Copyright (C) 2007 Rafael Fernández López <[email protected]> - * - * 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 diff --git a/src/views/dolphincolumnview.cpp b/src/views/dolphincolumnview.cpp deleted file mode 100644 index 3425c460b..000000000 --- a/src/views/dolphincolumnview.cpp +++ /dev/null @@ -1,621 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2007-2009 by Peter Penz <[email protected]> * - * * - * 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" diff --git a/src/views/dolphincolumnview.h b/src/views/dolphincolumnview.h deleted file mode 100644 index 2a4726afc..000000000 --- a/src/views/dolphincolumnview.h +++ /dev/null @@ -1,151 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2007-2009 by Peter Penz <[email protected]> * - * * - * 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 diff --git a/src/views/dolphincolumnviewcontainer.cpp b/src/views/dolphincolumnviewcontainer.cpp deleted file mode 100644 index 3216dd2b6..000000000 --- a/src/views/dolphincolumnviewcontainer.cpp +++ /dev/null @@ -1,427 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2007-2009 by Peter Penz <[email protected]> * - * * - * 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" diff --git a/src/views/dolphincolumnviewcontainer.h b/src/views/dolphincolumnviewcontainer.h deleted file mode 100644 index af6259982..000000000 --- a/src/views/dolphincolumnviewcontainer.h +++ /dev/null @@ -1,150 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2007-2009 by Peter Penz <[email protected]> * - * * - * 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 diff --git a/src/views/dolphindetailsview.cpp b/src/views/dolphindetailsview.cpp deleted file mode 100644 index 0ce26df33..000000000 --- a/src/views/dolphindetailsview.cpp +++ /dev/null @@ -1,706 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006 by Peter Penz ([email protected]) * - * Copyright (C) 2008 by Simon St. James ([email protected]) * - * * - * 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 <[email protected]> - // 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" diff --git a/src/views/dolphindetailsview.h b/src/views/dolphindetailsview.h deleted file mode 100644 index 305035dd5..000000000 --- a/src/views/dolphindetailsview.h +++ /dev/null @@ -1,224 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006-2010 by Peter Penz <[email protected]> * - * * - * 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 diff --git a/src/views/dolphindetailsviewexpander.cpp b/src/views/dolphindetailsviewexpander.cpp deleted file mode 100644 index cc4bc67be..000000000 --- a/src/views/dolphindetailsviewexpander.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009 by Frank Reininghaus ([email protected]) * - * * - * 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(); - } -} diff --git a/src/views/dolphindetailsviewexpander.h b/src/views/dolphindetailsviewexpander.h deleted file mode 100644 index b4dc2fc72..000000000 --- a/src/views/dolphindetailsviewexpander.h +++ /dev/null @@ -1,77 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009 by Frank Reininghaus ([email protected]) * - * * - * 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 diff --git a/src/views/dolphindirlister.cpp b/src/views/dolphindirlister.cpp index 568681fb7..b62abe167 100644 --- a/src/views/dolphindirlister.cpp +++ b/src/views/dolphindirlister.cpp @@ -21,8 +21,8 @@ #include <KLocale> #include <KIO/JobClasses> -DolphinDirLister::DolphinDirLister() : - KDirLister() +DolphinDirLister::DolphinDirLister(QObject* parent) : + KDirLister(parent) { setAutoErrorHandlingEnabled(false, 0); } diff --git a/src/views/dolphindirlister.h b/src/views/dolphindirlister.h index 662c7c0aa..0955df232 100644 --- a/src/views/dolphindirlister.h +++ b/src/views/dolphindirlister.h @@ -32,7 +32,7 @@ class LIBDOLPHINPRIVATE_EXPORT DolphinDirLister : public KDirLister Q_OBJECT public: - DolphinDirLister(); + DolphinDirLister(QObject* parent = 0); virtual ~DolphinDirLister(); signals: diff --git a/src/views/dolphinfileitemdelegate.cpp b/src/views/dolphinfileitemdelegate.cpp deleted file mode 100644 index 4d66c73f1..000000000 --- a/src/views/dolphinfileitemdelegate.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Peter Penz <[email protected]> * - * * - * 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]; -} - diff --git a/src/views/dolphinfileitemdelegate.h b/src/views/dolphinfileitemdelegate.h deleted file mode 100644 index ab4a9fd20..000000000 --- a/src/views/dolphinfileitemdelegate.h +++ /dev/null @@ -1,96 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Peter Penz <[email protected]> * - * * - * 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 DOLPHINFILEITEMDELEGATE_H -#define DOLPHINFILEITEMDELEGATE_H - -#include <KFileItemDelegate> -#include <views/dolphinmodel.h> - -class QAbstractProxyModel; - -/** - * Extends KFileItemDelegate by the ability to show the hover effect - * and the selection in a minimized way for the name column of - * the details view. - * - * Note that this is a workaround, as Qt does not support having custom - * shapes within the visual rect of an item view. The visual part of - * workaround is handled inside DolphinFileItemDelegate, the behavior - * changes are handled in DolphinTreeView. - */ -class DolphinFileItemDelegate : public KFileItemDelegate -{ - Q_OBJECT -public: - explicit DolphinFileItemDelegate(QObject* parent = 0); - virtual ~DolphinFileItemDelegate(); - - /** - * If \a minimized is true, the hover effect and the selection are - * only drawn above the icon and text of an item. Per default - * \a minimized is false, which means that the whole visual rect is - * used like in KFileItemDelegate. - */ - void setMinimizedNameColumn(bool minimized); - bool hasMinimizedNameColumn() const; - - virtual void paint(QPainter* painter, - const QStyleOptionViewItem& option, - const QModelIndex& index) const; - - /** - * Returns the minimized width of the name column for the name \a name. This method - * is also used in DolphinDetailsView to handle the selection of items correctly. - */ - static int nameColumnWidth(const QString& name, const QStyleOptionViewItem& option); - -private slots: - void handleDisplayPaletteChange(); - -private: - static void adjustOptionWidth(QStyleOptionViewItemV4& option, - const QAbstractProxyModel* proxyModel, - const DolphinModel* dolphinModel, - const QModelIndex& index); - - static void adjustOptionTextColor(QStyleOptionViewItemV4& option, - KVersionControlPlugin::VersionState state); - - QPixmap emblemForState(KVersionControlPlugin::VersionState state, const QSize& size) const; - -private: - bool m_hasMinimizedNameColumn; - mutable QSize m_cachedSize; - mutable QPixmap m_cachedEmblems[KVersionControlPlugin::LocallyModifiedUnstagedVersion + 1]; - mutable QColor m_cachedInactiveTextColor; - mutable bool m_cachedInactiveTextColorDirty; -}; - -inline void DolphinFileItemDelegate::setMinimizedNameColumn(bool minimized) -{ - m_hasMinimizedNameColumn = minimized; -} - -inline bool DolphinFileItemDelegate::hasMinimizedNameColumn() const -{ - return m_hasMinimizedNameColumn; -} - -#endif diff --git a/src/views/dolphiniconsview.cpp b/src/views/dolphiniconsview.cpp deleted file mode 100644 index a863de72d..000000000 --- a/src/views/dolphiniconsview.cpp +++ /dev/null @@ -1,524 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006-2009 by Peter Penz <[email protected]> * - * * - * 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 "dolphiniconsview.h" - -#include "dolphincategorydrawer.h" -#include "dolphinviewcontroller.h" -#include "settings/dolphinsettings.h" -#include "dolphinsortfilterproxymodel.h" -#include "dolphin_iconsmodesettings.h" -#include "dolphin_generalsettings.h" -#include "draganddrophelper.h" -#include "selectionmanager.h" -#include "viewextensionsfactory.h" -#include "viewmodecontroller.h" -#include "zoomlevelinfo.h" - -#include <KCategorizedSortFilterProxyModel> -#include <KDialog> -#include <KFileItemDelegate> - -#include <QAbstractProxyModel> -#include <QApplication> -#include <QScrollBar> - -DolphinIconsView::DolphinIconsView(QWidget* parent, - DolphinViewController* dolphinViewController, - const ViewModeController* viewModeController, - DolphinSortFilterProxyModel* proxyModel) : - KCategorizedView(parent), - m_dolphinViewController(dolphinViewController), - m_categoryDrawer(new DolphinCategoryDrawer(this)), - m_extensionsFactory(0), - m_font(), - m_decorationSize(), - m_decorationPosition(QStyleOptionViewItem::Top), - m_displayAlignment(Qt::AlignHCenter), - m_itemSize(), - m_dropRect() -{ - Q_ASSERT(dolphinViewController); - Q_ASSERT(viewModeController); - - setModel(proxyModel); - setLayoutDirection(Qt::LeftToRight); - setViewMode(QListView::IconMode); - setResizeMode(QListView::Adjust); - setMovement(QListView::Static); - setDragEnabled(true); - setEditTriggers(QAbstractItemView::NoEditTriggers); - viewport()->setAcceptDrops(true); - - setMouseTracking(true); - - 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&)), - dolphinViewController, SLOT(emitItemEntered(const QModelIndex&))); - connect(this, SIGNAL(viewportEntered()), - dolphinViewController, SLOT(emitViewportEntered())); - connect(viewModeController, SIGNAL(zoomLevelChanged(int)), - this, SLOT(setZoomLevel(int))); - - const DolphinView* view = dolphinViewController->view(); - connect(view, SIGNAL(showPreviewChanged()), - this, SLOT(slotShowPreviewChanged())); - connect(view, SIGNAL(additionalInfoChanged()), - this, SLOT(slotAdditionalInfoChanged())); - - // apply the icons mode settings to the widget - const IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings(); - 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()); - } - - setWordWrap(settings->numberOfTextlines() > 1); - - if (settings->arrangement() == QListView::TopToBottom) { - setFlow(QListView::LeftToRight); - m_decorationPosition = QStyleOptionViewItem::Top; - m_displayAlignment = Qt::AlignHCenter; - } else { - setFlow(QListView::TopToBottom); - m_decorationPosition = QStyleOptionViewItem::Left; - m_displayAlignment = Qt::AlignLeft | Qt::AlignVCenter; - } - - connect(m_categoryDrawer, SIGNAL(actionRequested(int,QModelIndex)), this, SLOT(categoryDrawerActionRequested(int,QModelIndex))); - setCategoryDrawer(m_categoryDrawer); - - connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), - this, SLOT(slotGlobalSettingsChanged(int))); - - updateGridSize(view->showPreview(), 0); - m_extensionsFactory = new ViewExtensionsFactory(this, dolphinViewController, viewModeController); -} - -DolphinIconsView::~DolphinIconsView() -{ -} - -void DolphinIconsView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) -{ - KCategorizedView::dataChanged(topLeft, bottomRight); - - KCategorizedSortFilterProxyModel* proxyModel = dynamic_cast<KCategorizedSortFilterProxyModel*>(model()); - if (!proxyModel->isCategorizedModel()) { - // bypass a QListView issue that items are not layout correctly if the decoration size of - // an index changes - scheduleDelayedItemsLayout(); - } -} - -QStyleOptionViewItem DolphinIconsView::viewOptions() const -{ - QStyleOptionViewItem viewOptions = KCategorizedView::viewOptions(); - viewOptions.font = m_font; - viewOptions.fontMetrics = QFontMetrics(m_font); - viewOptions.decorationPosition = m_decorationPosition; - viewOptions.decorationSize = m_decorationSize; - viewOptions.displayAlignment = m_displayAlignment; - viewOptions.showDecorationSelected = true; - return viewOptions; -} - -void DolphinIconsView::contextMenuEvent(QContextMenuEvent* event) -{ - KCategorizedView::contextMenuEvent(event); - m_dolphinViewController->triggerContextMenuRequest(event->pos()); -} - -void DolphinIconsView::mousePressEvent(QMouseEvent* event) -{ - m_dolphinViewController->requestActivation(); - const QModelIndex index = indexAt(event->pos()); - if (index.isValid() && (event->button() == Qt::LeftButton)) { - // TODO: It should not be necessary to manually set the dragging state, but I could - // not reproduce this issue with a Qt-only example yet to find the root cause. - // Issue description: start Dolphin, split the view and drag an item from the - // inactive view to the active view by a very fast mouse movement. Result: - // the item gets selected instead of being dragged... - setState(QAbstractItemView::DraggingState); - } - - if (!index.isValid() && (QApplication::mouseButtons() & Qt::MidButton)) { - m_dolphinViewController->replaceUrlByClipboard(); - } - - KCategorizedView::mousePressEvent(event); -} - -void DolphinIconsView::startDrag(Qt::DropActions supportedActions) -{ - DragAndDropHelper::instance().startDrag(this, supportedActions, m_dolphinViewController); -} - -void DolphinIconsView::dragEnterEvent(QDragEnterEvent* event) -{ - event->acceptProposedAction(); -} - -void DolphinIconsView::dragLeaveEvent(QDragLeaveEvent* event) -{ - KCategorizedView::dragLeaveEvent(event); - setDirtyRegion(m_dropRect); -} - -void DolphinIconsView::dragMoveEvent(QDragMoveEvent* event) -{ - KCategorizedView::dragMoveEvent(event); - - // TODO: remove this code when the issue #160611 is solved in Qt 4.4 - const QModelIndex index = indexAt(event->pos()); - setDirtyRegion(m_dropRect); - - m_dropRect.setSize(QSize()); // set as invalid - if (index.isValid()) { - const KFileItem item = m_dolphinViewController->itemForIndex(index); - if (!item.isNull() && item.isDir()) { - m_dropRect = visualRect(index); - } else { - m_dropRect.setSize(QSize()); // set as invalid - } - } - event->acceptProposedAction(); - - setDirtyRegion(m_dropRect); -} - -void DolphinIconsView::dropEvent(QDropEvent* event) -{ - const QModelIndex index = indexAt(event->pos()); - const KFileItem item = m_dolphinViewController->itemForIndex(index); - m_dolphinViewController->indicateDroppedUrls(item, event); - // don't call KCategorizedView::dropEvent(event), as it moves - // the items which is not wanted -} - -QModelIndex DolphinIconsView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) -{ - const QModelIndex oldCurrent = currentIndex(); - - QModelIndex newCurrent = KCategorizedView::moveCursor(cursorAction, modifiers); - if (newCurrent != oldCurrent) { - return newCurrent; - } - - // The cursor has not been moved by the base implementation. Provide a - // wrap behavior, so that the cursor will go to the next item when reaching - // the border. - const IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings(); - if (settings->arrangement() == QListView::LeftToRight) { - switch (cursorAction) { - case MoveUp: - if (newCurrent.row() == 0) { - return newCurrent; - } - newCurrent = KCategorizedView::moveCursor(MoveLeft, modifiers); - selectionModel()->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate); - newCurrent = KCategorizedView::moveCursor(MovePageDown, modifiers); - break; - - case MoveDown: - if (newCurrent.row() == (model()->rowCount() - 1)) { - return newCurrent; - } - newCurrent = KCategorizedView::moveCursor(MovePageUp, modifiers); - selectionModel()->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate); - newCurrent = KCategorizedView::moveCursor(MoveRight, modifiers); - break; - - default: - break; - } - } else { - QModelIndex current = oldCurrent; - switch (cursorAction) { - case MoveLeft: - if (newCurrent.row() == 0) { - return newCurrent; - } - newCurrent = KCategorizedView::moveCursor(MoveUp, modifiers); - do { - selectionModel()->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate); - current = newCurrent; - newCurrent = KCategorizedView::moveCursor(MoveRight, modifiers); - } while (newCurrent != current); - break; - - case MoveRight: - if (newCurrent.row() == (model()->rowCount() - 1)) { - return newCurrent; - } - do { - selectionModel()->setCurrentIndex(newCurrent, QItemSelectionModel::NoUpdate); - current = newCurrent; - newCurrent = KCategorizedView::moveCursor(MoveLeft, modifiers); - } while (newCurrent != current); - newCurrent = KCategorizedView::moveCursor(MoveDown, modifiers); - break; - - default: - break; - } - } - - // Revert all changes of the current item to make sure that item selection works correctly - selectionModel()->setCurrentIndex(oldCurrent, QItemSelectionModel::NoUpdate); - return newCurrent; -} - -void DolphinIconsView::keyPressEvent(QKeyEvent* event) -{ - KCategorizedView::keyPressEvent(event); - m_dolphinViewController->handleKeyPressEvent(event); -} - -void DolphinIconsView::wheelEvent(QWheelEvent* event) -{ - horizontalScrollBar()->setSingleStep(m_itemSize.width() / 5); - verticalScrollBar()->setSingleStep(m_itemSize.height() / 5); - - KCategorizedView::wheelEvent(event); - // if the icons are aligned left to right, the vertical wheel event should - // be applied to the horizontal scrollbar - const IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings(); - const bool scrollHorizontal = (event->orientation() == Qt::Vertical) && - (settings->arrangement() == QListView::LeftToRight); - if (scrollHorizontal) { - QWheelEvent horizEvent(event->pos(), - event->delta(), - event->buttons(), - event->modifiers(), - Qt::Horizontal); - QApplication::sendEvent(horizontalScrollBar(), &horizEvent); - } -} - -void DolphinIconsView::showEvent(QShowEvent* event) -{ - KFileItemDelegate* delegate = dynamic_cast<KFileItemDelegate*>(itemDelegate()); - delegate->setMaximumSize(m_itemSize); - - KCategorizedView::showEvent(event); -} - -void DolphinIconsView::leaveEvent(QEvent* event) -{ - KCategorizedView::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_dolphinViewController->emitViewportEntered(); -} - -void DolphinIconsView::currentChanged(const QModelIndex& current, const QModelIndex& previous) -{ - KCategorizedView::currentChanged(current, previous); - m_extensionsFactory->handleCurrentIndexChange(current, previous); -} - -void DolphinIconsView::resizeEvent(QResizeEvent* event) -{ - KCategorizedView::resizeEvent(event); - const DolphinView* view = m_dolphinViewController->view(); - updateGridSize(view->showPreview(), view->additionalInfo().count()); -} - -void DolphinIconsView::slotShowPreviewChanged() -{ - const DolphinView* view = m_dolphinViewController->view(); - updateGridSize(view->showPreview(), additionalInfoCount()); -} - -void DolphinIconsView::slotAdditionalInfoChanged() -{ - const DolphinView* view = m_dolphinViewController->view(); - const bool showPreview = view->showPreview(); - updateGridSize(showPreview, view->additionalInfo().count()); -} - -void DolphinIconsView::setZoomLevel(int level) -{ - IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings(); - - const int oldIconSize = settings->iconSize(); - int newIconSize = oldIconSize; - - const bool showPreview = m_dolphinViewController->view()->showPreview(); - if (showPreview) { - const int previewSize = ZoomLevelInfo::iconSizeForZoomLevel(level); - settings->setPreviewSize(previewSize); - } else { - newIconSize = ZoomLevelInfo::iconSizeForZoomLevel(level); - settings->setIconSize(newIconSize); - } - - // increase also the grid size - const int diff = newIconSize - oldIconSize; - settings->setItemWidth(settings->itemWidth() + diff); - settings->setItemHeight(settings->itemHeight() + diff); - - updateGridSize(showPreview, additionalInfoCount()); -} - -void DolphinIconsView::requestActivation() -{ - m_dolphinViewController->requestActivation(); -} - -void DolphinIconsView::slotGlobalSettingsChanged(int category) -{ - Q_UNUSED(category); - - const IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings(); - Q_ASSERT(settings); - if (settings->useSystemFont()) { - m_font = KGlobalSettings::generalFont(); - } - - 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 DolphinIconsView::categoryDrawerActionRequested(int action, const QModelIndex &index) -{ - const QSortFilterProxyModel *model = dynamic_cast<const QSortFilterProxyModel*>(index.model()); - const QModelIndex topLeft = model->index(index.row(), modelColumn()); - QModelIndex bottomRight = topLeft; - const QString category = model->data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString(); - QModelIndex current = topLeft; - while (true) { - current = model->index(current.row() + 1, modelColumn()); - const QString curCategory = model->data(model->index(current.row(), index.column()), KCategorizedSortFilterProxyModel::CategoryDisplayRole).toString(); - if (!current.isValid() || category != curCategory) { - break; - } - bottomRight = current; - } - switch (action) { - case DolphinCategoryDrawer::SelectAll: - selectionModel()->select(QItemSelection(topLeft, bottomRight), QItemSelectionModel::Select); - break; - case DolphinCategoryDrawer::UnselectAll: - selectionModel()->select(QItemSelection(topLeft, bottomRight), QItemSelectionModel::Deselect); - break; - default: - break; - } -} - -void DolphinIconsView::updateGridSize(bool showPreview, int additionalInfoCount) -{ - const IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings(); - Q_ASSERT(settings); - - int itemWidth = settings->itemWidth(); - int itemHeight = settings->itemHeight(); - int size = settings->iconSize(); - - if (showPreview) { - const int previewSize = settings->previewSize(); - const int diff = previewSize - size; - itemWidth += diff; - itemHeight += diff; - - size = previewSize; - } - - Q_ASSERT(additionalInfoCount >= 0); - itemHeight += additionalInfoCount * QFontMetrics(m_font).height(); - - // Optimize the item size of the grid in a way to prevent large gaps on the - // right border (= row arrangement) or the bottom border (= column arrangement). - // There is no public API in QListView to find out the used width of the viewport - // for the layout. The following calculation of 'contentWidth'/'contentHeight' - // is based on QListViewPrivate::prepareItemsLayout() (Copyright (C) 2009 Nokia Corporation). - int frameAroundContents = 0; - if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) { - frameAroundContents = style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2; - } - const int spacing = settings->gridSpacing(); - if (settings->arrangement() == QListView::TopToBottom) { - const int contentWidth = viewport()->width() - 1 - - frameAroundContents - - style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, horizontalScrollBar()); - const int gridWidth = itemWidth + spacing * 2; - const int horizItemCount = contentWidth / gridWidth; - if (horizItemCount > 0) { - itemWidth += (contentWidth - horizItemCount * gridWidth) / horizItemCount; - } - - // The decoration width indirectly defines the maximum - // width for the text wrapping. To use the maximum item width - // for text wrapping, it is used as decoration width. - m_decorationSize = QSize(itemWidth, size); - setIconSize(QSize(itemWidth, size)); - } else { - const int contentHeight = viewport()->height() - 1 - - frameAroundContents - - style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, verticalScrollBar()); - const int gridHeight = itemHeight + spacing; - const int vertItemCount = contentHeight / gridHeight; - if (vertItemCount > 0) { - itemHeight += (contentHeight - vertItemCount * gridHeight) / vertItemCount; - } - - m_decorationSize = QSize(size, size); - setIconSize(QSize(size, size)); - } - - m_itemSize = QSize(itemWidth, itemHeight); - setGridSizeOwn(QSize(itemWidth + spacing * 2, itemHeight + spacing)); - - KFileItemDelegate* delegate = dynamic_cast<KFileItemDelegate*>(itemDelegate()); - if (delegate) { - delegate->setMaximumSize(m_itemSize); - } -} - -int DolphinIconsView::additionalInfoCount() const -{ - const DolphinView* view = m_dolphinViewController->view(); - return view->additionalInfo().count(); -} - -#include "dolphiniconsview.moc" diff --git a/src/views/dolphiniconsview.h b/src/views/dolphiniconsview.h deleted file mode 100644 index 2dd1aa79f..000000000 --- a/src/views/dolphiniconsview.h +++ /dev/null @@ -1,121 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006-2009 by Peter Penz <[email protected]> * - * * - * 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 DOLPHINICONSVIEW_H -#define DOLPHINICONSVIEW_H - -#include <KCategorizedView> - -#include <KFileItem> -#include <KFileItemDelegate> - -#include <QFont> -#include <QSize> -#include <QStyleOption> - -#include <libdolphin_export.h> - -class DolphinViewController; -class DolphinCategoryDrawer; -class DolphinSortFilterProxyModel; -class ViewExtensionsFactory; -class ViewModeController; - -/** - * @brief Represents the view, where each item is shown as an icon. - * - * It is also possible that instead of the icon a preview of the item - * content is shown. - */ -class LIBDOLPHINPRIVATE_EXPORT DolphinIconsView : public KCategorizedView -{ - Q_OBJECT - -public: - /** - * @param parent Parent widget. - * @param dolphinViewController Allows the DolphinIconsView to control the - * DolphinView in a limited way. - * @param viewModeController Controller that is used by the DolphinView - * to control the DolphinIconsView. The DolphinIconsView - * only has read access to the controller. - * @param model Directory that is shown. - */ - explicit DolphinIconsView(QWidget* parent, - DolphinViewController* dolphinViewController, - const ViewModeController* viewModeController, - DolphinSortFilterProxyModel* proxyModel); - virtual ~DolphinIconsView(); - -protected slots: - virtual void dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight); - -protected: - 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 dragLeaveEvent(QDragLeaveEvent* event); - virtual void dragMoveEvent(QDragMoveEvent* event); - virtual void dropEvent(QDropEvent* event); - virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers); - virtual void keyPressEvent(QKeyEvent* event); - virtual void wheelEvent(QWheelEvent* event); - virtual void showEvent(QShowEvent* event); - virtual void leaveEvent(QEvent* event); - virtual void currentChanged(const QModelIndex& current, const QModelIndex& previous); - virtual void resizeEvent(QResizeEvent* event); - -private slots: - void slotShowPreviewChanged(); - void slotAdditionalInfoChanged(); - void setZoomLevel(int level); - void requestActivation(); - void slotGlobalSettingsChanged(int category); - void categoryDrawerActionRequested(int action, const QModelIndex &index); - -private: - /** - * Updates the size of the grid depending on the state - * of \a showPreview and \a additionalInfoCount. - */ - void updateGridSize(bool showPreview, int additionalInfoCount); - - /** - * Returns the number of additional information lines that should - * be shown below the item name. - */ - int additionalInfoCount() const; - -private: - DolphinViewController* m_dolphinViewController; - DolphinCategoryDrawer* m_categoryDrawer; - ViewExtensionsFactory* m_extensionsFactory; - - QFont m_font; - QSize m_decorationSize; - QStyleOptionViewItem::Position m_decorationPosition; - Qt::Alignment m_displayAlignment; - - QSize m_itemSize; - QRect m_dropRect; -}; - -#endif diff --git a/src/views/dolphinitemlistcontainer.cpp b/src/views/dolphinitemlistcontainer.cpp new file mode 100644 index 000000000..9c9ad6de3 --- /dev/null +++ b/src/views/dolphinitemlistcontainer.cpp @@ -0,0 +1,279 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 "dolphinitemlistcontainer.h" + +#include "dolphin_iconsmodesettings.h" +#include "dolphin_detailsmodesettings.h" +#include "dolphin_compactmodesettings.h" + +#include <kitemviews/kitemliststyleoption.h> +#include <kitemviews/kfileitemlistview.h> +#include <kitemviews/kfileitemmodel.h> +#include <kitemviews/kitemlistcontroller.h> +#include <kitemviews/kitemliststyleoption.h> + +#include <KDirLister> +#include <KGlobalSettings> + +#include <views/viewmodecontroller.h> + +#include "zoomlevelinfo.h" + + +DolphinItemListContainer::DolphinItemListContainer(KDirLister* dirLister, + QWidget* parent) : + KItemListContainer(parent), + m_zoomLevel(0), + m_fileItemListView(0) +{ + controller()->setModel(new KFileItemModel(dirLister, this)); + + m_fileItemListView = new KFileItemListView(); + controller()->setView(m_fileItemListView); + + KItemListStyleOption option; + + // TODO: + option.font = parent->font(); + option.fontMetrics = QFontMetrics(parent->font()); + + updateGridSize(); +/* + connect(this, SIGNAL(clicked(const QModelIndex&)), + dolphinViewController, SLOT(requestTab(const QModelIndex&)));*/ +/* + connect(this, SIGNAL(entered(const QModelIndex&)), + dolphinViewController, SLOT(emitItemEntered(const QModelIndex&))); + connect(this, SIGNAL(viewportEntered()), + dolphinViewController, SLOT(emitViewportEntered()));*/ + + // apply the icons mode settings to the widget + //const IconsModeSettings* settings = DolphinSettings::instance().iconsModeSettings(); + //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()); + } + + setWordWrap(settings->numberOfTextlines() > 1); + + if (settings->arrangement() == QListView::TopToBottom) { + setFlow(QListView::LeftToRight); + m_decorationPosition = QStyleOptionViewItem::Top; + m_displayAlignment = Qt::AlignHCenter; + } else { + setFlow(QListView::TopToBottom); + m_decorationPosition = QStyleOptionViewItem::Left; + m_displayAlignment = Qt::AlignLeft | Qt::AlignVCenter; + } + + connect(m_categoryDrawer, SIGNAL(actionRequested(int,QModelIndex)), this, SLOT(categoryDrawerActionRequested(int,QModelIndex))); + setCategoryDrawer(m_categoryDrawer); + + connect(KGlobalSettings::self(), SIGNAL(settingsChanged(int)), + this, SLOT(slotGlobalSettingsChanged(int)));*/ + + //updateGridSize(dolphinView->showPreview(), 0); + /*m_extensionsFactory = new ViewExtensionsFactory(this, dolphinViewController, viewModeController);*/ +} + +DolphinItemListContainer::~DolphinItemListContainer() +{ + IconsModeSettings::self()->writeConfig(); + CompactModeSettings::self()->writeConfig(); + DetailsModeSettings::self()->writeConfig(); + + KItemListView* view = controller()->view(); + controller()->setView(0); + delete view; +} + +void DolphinItemListContainer::setPreviewsShown(bool show) +{ + beginTransaction(); + m_fileItemListView->setPreviewsShown(show); + updateGridSize(); + endTransaction(); +} + +bool DolphinItemListContainer::previewsShown() const +{ + return m_fileItemListView->previewsShown(); +} + +void DolphinItemListContainer::setVisibleRoles(const QHash<QByteArray, int>& roles) +{ + m_fileItemListView->setVisibleRoles(roles); + updateGridSize(); +} + +QHash<QByteArray, int> DolphinItemListContainer::visibleRoles() const +{ + return m_fileItemListView->visibleRoles(); +} + +void DolphinItemListContainer::setZoomLevel(int level) +{ + if (level < ZoomLevelInfo::minimumLevel()) { + level = ZoomLevelInfo::minimumLevel(); + } else if (level > ZoomLevelInfo::maximumLevel()) { + level = ZoomLevelInfo::maximumLevel(); + } + + if (level == m_zoomLevel) { + return; + } + + m_zoomLevel = level; + + if (previewsShown()) { + const int previewSize = ZoomLevelInfo::iconSizeForZoomLevel(level); + + switch (itemLayout()) { + case KFileItemListView::IconsLayout: IconsModeSettings::setPreviewSize(previewSize); break; + case KFileItemListView::CompactLayout: CompactModeSettings::setPreviewSize(previewSize); break; + case KFileItemListView::DetailsLayout: DetailsModeSettings::setPreviewSize(previewSize); break; + default: Q_ASSERT(false); break; + } + } else { + const int iconSize = ZoomLevelInfo::iconSizeForZoomLevel(level); + switch (itemLayout()) { + case KFileItemListView::IconsLayout: IconsModeSettings::setIconSize(iconSize); break; + case KFileItemListView::CompactLayout: CompactModeSettings::setIconSize(iconSize); break; + case KFileItemListView::DetailsLayout: DetailsModeSettings::setIconSize(iconSize); break; + default: Q_ASSERT(false); break; + } + } + + updateGridSize(); +} + +int DolphinItemListContainer::zoomLevel() const +{ + return m_zoomLevel; +} + +void DolphinItemListContainer::setItemLayout(KFileItemListView::Layout layout) +{ + if (layout == itemLayout()) { + return; + } + + beginTransaction(); + m_fileItemListView->setItemLayout(layout); + + switch (layout) { + case KFileItemListView::IconsLayout: + case KFileItemListView::DetailsLayout: + m_fileItemListView->setScrollOrientation(Qt::Vertical); + break; + case KFileItemListView::CompactLayout: + m_fileItemListView->setScrollOrientation(Qt::Horizontal); + break; + default: + Q_ASSERT(false); + break; + } + + updateGridSize(); + endTransaction(); +} + +KFileItemListView::Layout DolphinItemListContainer::itemLayout() const +{ + return m_fileItemListView->itemLayout(); +} + +void DolphinItemListContainer::beginTransaction() +{ + m_fileItemListView->beginTransaction(); +} + +void DolphinItemListContainer::endTransaction() +{ + m_fileItemListView->endTransaction(); +} + +void DolphinItemListContainer::updateGridSize() +{ + // Calculate the size of the icon + int iconSize; + if (previewsShown()) { + switch (itemLayout()) { + case KFileItemListView::IconsLayout: iconSize = IconsModeSettings::previewSize(); break; + case KFileItemListView::CompactLayout: iconSize = CompactModeSettings::previewSize(); break; + case KFileItemListView::DetailsLayout: iconSize = DetailsModeSettings::previewSize(); break; + default: Q_ASSERT(false); break; + } + } else { + switch (itemLayout()) { + case KFileItemListView::IconsLayout: iconSize = IconsModeSettings::iconSize(); break; + case KFileItemListView::CompactLayout: iconSize = CompactModeSettings::iconSize(); break; + case KFileItemListView::DetailsLayout: iconSize = DetailsModeSettings::iconSize(); break; + default: Q_ASSERT(false); break; + } + } + + m_zoomLevel = ZoomLevelInfo::zoomLevelForIconSize(QSize(iconSize, iconSize)); + KItemListStyleOption styleOption = m_fileItemListView->styleOption(); + + const int innerMargin = (iconSize >= KIconLoader::SizeSmallMedium) ? 4 : 2; + + // Calculate the item-width and item-height + int itemWidth; + int itemHeight; + switch (itemLayout()) { + case KFileItemListView::IconsLayout: { + const int minItemWidth = 64; + itemWidth = minItemWidth + IconsModeSettings::textWidthIndex() * 64; // TODO: + if (itemWidth < iconSize + innerMargin * 2) { + itemWidth = iconSize + innerMargin * 2; + } + itemHeight = innerMargin * 2 + iconSize + styleOption.fontMetrics.height(); + break; + } + case KFileItemListView::CompactLayout: { + itemWidth = innerMargin * 2; + const int textLinesCount = m_fileItemListView->visibleRoles().count(); + itemHeight = innerMargin * 2 + qMax(iconSize, textLinesCount * styleOption.fontMetrics.height()); + break; + } + case KFileItemListView::DetailsLayout: { + itemWidth = -1; + itemHeight = innerMargin * 2 + qMax(iconSize, styleOption.fontMetrics.height()); + break; + } + default: Q_ASSERT(false); break; + } + + // Apply the calculated values + styleOption.margin = innerMargin; + styleOption.iconSize = iconSize; + m_fileItemListView->setStyleOption(styleOption); + m_fileItemListView->setItemSize(QSizeF(itemWidth, itemHeight)); +} + +#include "dolphinitemlistcontainer.moc" diff --git a/src/views/dolphinitemlistcontainer.h b/src/views/dolphinitemlistcontainer.h new file mode 100644 index 000000000..599a4bd74 --- /dev/null +++ b/src/views/dolphinitemlistcontainer.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2011 by Peter Penz <[email protected]> * + * * + * 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 DOLPHINITEMLISTCONTAINER_H +#define DOLPHINITEMLISTCONTAINER_H + +#include <kitemviews/kfileitemlistview.h> +#include <kitemviews/kitemlistcontainer.h> + +#include <libdolphin_export.h> + +class KDirLister; +class KFileItemListView; + +/** + * @brief Extends KItemListContainer by Dolphin specific properties. + * + * The view and model for KFileItems are created automatically when + * instantating KItemListContainer. + * + * The Dolphin settings of the icons-, compact- and details-view are + * converted internally to properties that can be used to configure e.g. + * the item-size and visible roles of the KItemListView. + */ +class LIBDOLPHINPRIVATE_EXPORT DolphinItemListContainer : public KItemListContainer +{ + Q_OBJECT + +public: + DolphinItemListContainer(KDirLister* dirLister, + QWidget* parent = 0); + + virtual ~DolphinItemListContainer(); + + void setPreviewsShown(bool show); + bool previewsShown() 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; + + void setZoomLevel(int level); + int zoomLevel() const; + + void setItemLayout(KFileItemListView::Layout layout); + KFileItemListView::Layout itemLayout() const; + + void beginTransaction(); + void endTransaction(); + +private: + void updateGridSize(); + +private: + int m_zoomLevel; + KFileItemListView* m_fileItemListView; +}; + +#endif diff --git a/src/views/dolphinmodel.cpp b/src/views/dolphinmodel.cpp deleted file mode 100644 index c999dfacd..000000000 --- a/src/views/dolphinmodel.cpp +++ /dev/null @@ -1,448 +0,0 @@ -/** - * This file is part of the KDE project - * Copyright (C) 2007 Rafael Fernández López <[email protected]> - * - * 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 "dolphinmodel.h" - -#include "dolphinsortfilterproxymodel.h" - -#include <KCategorizedView> -#include <KDateTime> -#include <KDirModel> -#include <KFileItem> -#include <KIconLoader> -#include <KLocale> -#include <KUrl> -#include <KUser> -#include <KMimeType> -#include <KStandardDirs> - -#include <QList> -#include <QSortFilterProxyModel> -#include <QPainter> -#include <QPersistentModelIndex> -#include <QDir> -#include <QFileInfo> - -const char* const DolphinModel::m_others = I18N_NOOP2("@title:group Name", "Others"); - -DolphinModel::DolphinModel(QObject* parent) : - KDirModel(parent), - m_hasVersionData(false), - m_revisionHash() -{ - setJobTransfersVisible(true); -} - -DolphinModel::~DolphinModel() -{ -} - -bool DolphinModel::setData(const QModelIndex& index, const QVariant& value, int role) -{ - if ((index.column() == DolphinModel::Version) && (role == Qt::DecorationRole)) { - // TODO: remove data again when items are deleted... - - const QPersistentModelIndex key = index; - const KVersionControlPlugin::VersionState state = static_cast<KVersionControlPlugin::VersionState>(value.toInt()); - if (m_revisionHash.value(key, KVersionControlPlugin::UnversionedVersion) != state) { - if (!m_hasVersionData) { - connect(this, SIGNAL(rowsRemoved (const QModelIndex&, int, int)), - this, SLOT(slotRowsRemoved(const QModelIndex&, int, int))); - m_hasVersionData = true; - } - - m_revisionHash.insert(key, state); - emit dataChanged(index, index); - return true; - } - } - - return KDirModel::setData(index, value, role); -} - -QVariant DolphinModel::data(const QModelIndex& index, int role) const -{ - switch (role) { - case KCategorizedSortFilterProxyModel::CategoryDisplayRole: - return displayRoleData(index); - - case KCategorizedSortFilterProxyModel::CategorySortRole: - return sortRoleData(index); - - case Qt::DecorationRole: - if (index.column() == DolphinModel::Version) { - return m_revisionHash.value(index, KVersionControlPlugin::UnversionedVersion); - } - break; - - case Qt::DisplayRole: - switch (index.column()) { - case DolphinModel::LinkDest: { - const KDirModel *dirModel = qobject_cast<const KDirModel*>(index.model()); - const KFileItem item = dirModel->itemForIndex(index); - return item.linkDest(); - } - - case DolphinModel::LocalPathOrUrl: - const KDirModel *dirModel = qobject_cast<const KDirModel*>(index.model()); - const KFileItem item = dirModel->itemForIndex(index); - const KUrl url = item.mostLocalUrl(); - if (url.protocol() == QLatin1String("trash")) { - const KIO::UDSEntry udsEntry = item.entry(); - return udsEntry.stringValue(KIO::UDSEntry::UDS_EXTRA); - } - return url.directory(); - } - break; - - default: - break; - } - - return KDirModel::data(index, role); -} - -QVariant DolphinModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if ((orientation == Qt::Horizontal) && (role == Qt::DisplayRole)) { - switch (section) { - case DolphinModel::LinkDest: - return i18nc("@title::column", "Link Destination"); - case DolphinModel::LocalPathOrUrl: - return i18nc("@title::column", "Path"); - default: - return KDirModel::headerData(section, orientation, role); - } - } - return QVariant(); -} - -int DolphinModel::columnCount(const QModelIndex& parent) const -{ - return KDirModel::columnCount(parent) + (ExtraColumnCount - ColumnCount); -} - -void DolphinModel::clearVersionData() -{ - m_revisionHash.clear(); - m_hasVersionData = false; -} - -bool DolphinModel::hasVersionData() const -{ - return m_hasVersionData; -} - -void DolphinModel::slotRowsRemoved(const QModelIndex& parent, int start, int end) -{ - if (m_hasVersionData) { - const int column = parent.column(); - for (int row = start; row <= end; ++row) { - m_revisionHash.remove(parent.child(row, column)); - } - } -} - -QVariant DolphinModel::displayRoleData(const QModelIndex& index) const -{ - QString retString; - - if (!index.isValid()) { - return retString; - } - - const KDirModel *dirModel = qobject_cast<const KDirModel*>(index.model()); - KFileItem item = dirModel->itemForIndex(index); - - switch (index.column()) { - case KDirModel::Name: { - // KDirModel checks columns to know to which role are - // we talking about - const QModelIndex nameIndex = index.model()->index(index.row(), KDirModel::Name, index.parent()); - if (!nameIndex.isValid()) { - return retString; - } - const QVariant data = nameIndex.model()->data(nameIndex, Qt::DisplayRole); - const QString name = data.toString(); - if (!name.isEmpty()) { - if (!item.isHidden() && name.at(0).isLetter()) - retString = name.at(0).toUpper(); - else if (item.isHidden()) { - if (name.at(0) == '.') { - if (name.size() > 1 && name.at(1).isLetter()) { - retString = name.at(1).toUpper(); - } else { - retString = i18nc("@title:group Name", m_others); - } - } else { - retString = name.at(0).toUpper(); - } - } else { - bool validCategory = false; - - const QString str(name.toUpper()); - const QChar* currA = str.unicode(); - while (!currA->isNull() && !validCategory) { - if (currA->isLetter()) { - validCategory = true; - } else if (currA->isDigit()) { - return i18nc("@title:group Name", m_others); - } else { - ++currA; - } - } - - retString = validCategory ? *currA : i18nc("@title:group Name", m_others); - } - } - break; - } - - case KDirModel::Size: { - const KIO::filesize_t fileSize = !item.isNull() ? item.size() : ~0U; - if (!item.isNull() && item.isDir()) { - retString = i18nc("@title:group Size", "Folders"); - } else if (fileSize < 5242880) { - retString = i18nc("@title:group Size", "Small"); - } else if (fileSize < 10485760) { - retString = i18nc("@title:group Size", "Medium"); - } else { - retString = i18nc("@title:group Size", "Big"); - } - break; - } - - case KDirModel::ModifiedTime: { - KDateTime modifiedTime = item.time(KFileItem::ModificationTime); - modifiedTime = modifiedTime.toLocalZone(); - - const QDate currentDate = KDateTime::currentLocalDateTime().date(); - const QDate modifiedDate = modifiedTime.date(); - - const int daysDistance = modifiedDate.daysTo(currentDate); - - int yearForCurrentWeek = 0; - int currentWeek = currentDate.weekNumber(&yearForCurrentWeek); - if (yearForCurrentWeek == currentDate.year() + 1) { - currentWeek = 53; - } - - int yearForModifiedWeek = 0; - int modifiedWeek = modifiedDate.weekNumber(&yearForModifiedWeek); - if (yearForModifiedWeek == modifiedDate.year() + 1) { - modifiedWeek = 53; - } - - if (currentDate.year() == modifiedDate.year() && currentDate.month() == modifiedDate.month()) { - if (modifiedWeek > currentWeek) { - // use case: modified date = 2010-01-01, current date = 2010-01-22 - // modified week = 53, current week = 3 - modifiedWeek = 0; - } - switch (currentWeek - modifiedWeek) { - case 0: - switch (daysDistance) { - case 0: retString = i18nc("@title:group Date", "Today"); break; - case 1: retString = i18nc("@title:group Date", "Yesterday"); break; - default: retString = modifiedTime.toString(i18nc("@title:group The week day name: %A", "%A")); - } - break; - case 1: - retString = i18nc("@title:group Date", "Last Week"); - break; - case 2: - retString = i18nc("@title:group Date", "Two Weeks Ago"); - break; - case 3: - retString = i18nc("@title:group Date", "Three Weeks Ago"); - break; - case 4: - case 5: - retString = i18nc("@title:group Date", "Earlier this Month"); - break; - default: - Q_ASSERT(false); - } - } else { - const QDate lastMonthDate = currentDate.addMonths(-1); - if (lastMonthDate.year() == modifiedDate.year() && lastMonthDate.month() == modifiedDate.month()) { - if (daysDistance == 1) { - retString = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Yesterday (%B, %Y)")); - } else if (daysDistance <= 7) { - retString = modifiedTime.toString(i18nc("@title:group The week day name: %A, %B is full month name in current locale, and %Y is full year number", "%A (%B, %Y)")); - } else if (daysDistance <= 7 * 2) { - retString = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Last Week (%B, %Y)")); - } else if (daysDistance <= 7 * 3) { - retString = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Two Weeks Ago (%B, %Y)")); - } else if (daysDistance <= 7 * 4) { - retString = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Three Weeks Ago (%B, %Y)")); - } else { - retString = modifiedTime.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Earlier on %B, %Y")); - } - } else { - retString = modifiedTime.toString(i18nc("@title:group The month and year: %B is full month name in current locale, and %Y is full year number", "%B, %Y")); - } - } - break; - } - - case KDirModel::Permissions: { - QString user; - QString group; - QString others; - - QFileInfo info(item.url().pathOrUrl()); - - // set user string - if (info.permission(QFile::ReadUser)) { - user = i18nc("@item:intext Access permission, concatenated", "Read, "); - } - if (info.permission(QFile::WriteUser)) { - user += i18nc("@item:intext Access permission, concatenated", "Write, "); - } - if (info.permission(QFile::ExeUser)) { - user += i18nc("@item:intext Access permission, concatenated", "Execute, "); - } - user = user.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : user.mid(0, user.count() - 2); - - // set group string - if (info.permission(QFile::ReadGroup)) { - group = i18nc("@item:intext Access permission, concatenated", "Read, "); - } - if (info.permission(QFile::WriteGroup)) { - group += i18nc("@item:intext Access permission, concatenated", "Write, "); - } - if (info.permission(QFile::ExeGroup)) { - group += i18nc("@item:intext Access permission, concatenated", "Execute, "); - } - group = group.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : group.mid(0, group.count() - 2); - - // set permission string - if (info.permission(QFile::ReadOther)) { - others = i18nc("@item:intext Access permission, concatenated", "Read, "); - } - if (info.permission(QFile::WriteOther)) { - others += i18nc("@item:intext Access permission, concatenated", "Write, "); - } - if (info.permission(QFile::ExeOther)) { - others += i18nc("@item:intext Access permission, concatenated", "Execute, "); - } - others = others.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : others.mid(0, others.count() - 2); - - retString = i18nc("@title:group Files and folders by permissions", "(User: %1) (Group: %2) (Others: %3)", user, group, others); - break; - } - - case KDirModel::Owner: - retString = item.user(); - break; - - case KDirModel::Group: - retString = item.group(); - break; - - case KDirModel::Type: - retString = item.mimeComment(); - break; - - case DolphinModel::Version: - retString = "test"; - break; - } - - return retString; -} - -QVariant DolphinModel::sortRoleData(const QModelIndex& index) const -{ - QVariant retVariant; - - if (!index.isValid()) { - return retVariant; - } - - const KDirModel *dirModel = qobject_cast<const KDirModel*>(index.model()); - KFileItem item = dirModel->itemForIndex(index); - - switch (index.column()) { - case KDirModel::Name: { - retVariant = data(index, KCategorizedSortFilterProxyModel::CategoryDisplayRole); - if (retVariant == i18nc("@title:group Name", m_others)) { - // assure that the "Others" group is always the last categorization - retVariant = QString('Z').append(QChar::ReplacementCharacter); - } - break; - } - - case KDirModel::Size: { - const KIO::filesize_t fileSize = !item.isNull() ? item.size() : ~0U; - if (item.isDir()) { - retVariant = 0; - } else if (fileSize < 5242880) { - retVariant = 1; - } else if (fileSize < 10485760) { - retVariant = 2; - } else { - retVariant = 3; - } - break; - } - - case KDirModel::ModifiedTime: { - KDateTime modifiedTime = item.time(KFileItem::ModificationTime); - modifiedTime = modifiedTime.toLocalZone(); - - const QDate currentDate = KDateTime::currentLocalDateTime().date(); - const QDate modifiedDate = modifiedTime.date(); - - retVariant = -modifiedDate.daysTo(currentDate); - break; - } - - case KDirModel::Permissions: { - QFileInfo info(item.url().pathOrUrl()); - - retVariant = -KDirSortFilterProxyModel::pointsForPermissions(info); - break; - } - - case KDirModel::Owner: - retVariant = item.user(); - break; - - case KDirModel::Group: - retVariant = item.group(); - break; - - case KDirModel::Type: - if (item.isDir()) { - // when sorting we want folders to be placed first - retVariant = QString(); // krazy:exclude=nullstrassign - } else { - retVariant = item.mimeComment(); - } - break; - - default: - break; - } - - return retVariant; -} diff --git a/src/views/dolphinmodel.h b/src/views/dolphinmodel.h deleted file mode 100644 index 2f6d1f02d..000000000 --- a/src/views/dolphinmodel.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of the KDE project - * Copyright (C) 2007 Rafael Fernández López <[email protected]> - * - * 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 DOLPHINMODEL_H -#define DOLPHINMODEL_H - -#include <KDirModel> -#include <kversioncontrolplugin.h> -#include <libdolphin_export.h> - -#include <QHash> -#include <QPersistentModelIndex> - -class LIBDOLPHINPRIVATE_EXPORT DolphinModel : public KDirModel -{ - Q_OBJECT - -public: - enum AdditionalColumns { - LinkDest = KDirModel::ColumnCount, - LocalPathOrUrl, - // Assure that invisible columns are added as last entries: - Version, - ExtraColumnCount // Mandatory last entry - }; - - DolphinModel(QObject* parent = 0); - virtual ~DolphinModel(); - - virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); - virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - virtual int columnCount(const QModelIndex& parent = QModelIndex()) const; - - void clearVersionData(); - bool hasVersionData() const; - -private slots: - void slotRowsRemoved(const QModelIndex& parent, int start, int end); - -private: - QVariant displayRoleData(const QModelIndex& index) const; - QVariant sortRoleData(const QModelIndex& index) const; - -private: - bool m_hasVersionData; - QHash<QPersistentModelIndex, KVersionControlPlugin::VersionState> m_revisionHash; - - static const char* const m_others; -}; - -#endif // DOLPHINMODEL_H diff --git a/src/views/dolphinsortfilterproxymodel.cpp b/src/views/dolphinsortfilterproxymodel.cpp deleted file mode 100644 index a2d513ba4..000000000 --- a/src/views/dolphinsortfilterproxymodel.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006-2010 by Peter Penz <[email protected]> * - * Copyright (C) 2006 by Dominic Battre <[email protected]> * - * Copyright (C) 2006 by Martin Pool <[email protected]> * - * Copyright (C) 2007 by Rafael Fernández López <[email protected]> * - * * - * 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 "dolphinsortfilterproxymodel.h" - -DolphinSortFilterProxyModel::DolphinSortFilterProxyModel(QObject* parent) : - KDirSortFilterProxyModel(parent), - m_sorting(DolphinView::SortByName), - m_sortOrder(Qt::AscendingOrder) -{ -} - -DolphinSortFilterProxyModel::~DolphinSortFilterProxyModel() -{ -} - -void DolphinSortFilterProxyModel::setSorting(DolphinView::Sorting sorting) -{ - m_sorting = sorting; - KDirSortFilterProxyModel::sort(static_cast<int>(m_sorting), m_sortOrder); -} - -void DolphinSortFilterProxyModel::setSortOrder(Qt::SortOrder sortOrder) -{ - m_sortOrder = sortOrder; - KDirSortFilterProxyModel::sort(static_cast<int>(m_sorting), m_sortOrder); -} - -void DolphinSortFilterProxyModel::setSortFoldersFirst(bool foldersFirst) -{ - if (foldersFirst != sortFoldersFirst()) { - KDirSortFilterProxyModel::setSortFoldersFirst(foldersFirst); - - // We need to make sure that the files and folders are really resorted. - // Without the following two lines, QSortFilterProxyModel::sort(int column, Qt::SortOrder order) - // would do nothing because neither the column nor the sort order have changed. - // TODO: remove this hack if we find a better way to force the ProxyModel to re-sort the data. - const Qt::SortOrder tmpSortOrder = (m_sortOrder == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder); - KDirSortFilterProxyModel::sort(static_cast<int>(m_sorting), tmpSortOrder); - - // Now comes the real sorting with the old column and sort order - KDirSortFilterProxyModel::sort(static_cast<int>(m_sorting), m_sortOrder); - } -} - -void DolphinSortFilterProxyModel::sort(int column, Qt::SortOrder sortOrder) -{ - m_sorting = sortingForColumn(column); - m_sortOrder = sortOrder; - - emit sortingRoleChanged(); - KDirSortFilterProxyModel::sort(static_cast<int>(m_sorting), sortOrder); -} - -DolphinView::Sorting DolphinSortFilterProxyModel::sortingForColumn(int column) -{ - Q_ASSERT(column >= 0); - Q_ASSERT(column <= DolphinView::MaxSortingEnum); - return static_cast<DolphinView::Sorting>(column); -} - -#include "dolphinsortfilterproxymodel.moc" diff --git a/src/views/dolphinsortfilterproxymodel.h b/src/views/dolphinsortfilterproxymodel.h deleted file mode 100644 index a4770e85e..000000000 --- a/src/views/dolphinsortfilterproxymodel.h +++ /dev/null @@ -1,85 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006-2010 by Peter Penz <[email protected]> * - * * - * 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 DOLPHINSORTFILTERPROXYMODEL_H -#define DOLPHINSORTFILTERPROXYMODEL_H - -#include <views/dolphinview.h> -#include <kdirsortfilterproxymodel.h> -#include <libdolphin_export.h> - -/** - * @brief Acts as proxy model for DolphinModel to sort and filter - * KFileItems. - * - * Per default a natural sorting is done. This means that items like: - * - item_10.png - * - item_1.png - * - item_2.png - * are sorted like - * - item_1.png - * - item_2.png - * - item_10.png - */ -class LIBDOLPHINPRIVATE_EXPORT DolphinSortFilterProxyModel : public KDirSortFilterProxyModel -{ - Q_OBJECT - -public: - DolphinSortFilterProxyModel(QObject* parent = 0); - virtual ~DolphinSortFilterProxyModel(); - - void setSorting(DolphinView::Sorting sorting); - DolphinView::Sorting sorting() const; - - void setSortOrder(Qt::SortOrder sortOrder); - Qt::SortOrder sortOrder() const; - - void setSortFoldersFirst(bool foldersFirst); - - /** @reimplemented */ - virtual void sort(int column, - Qt::SortOrder order = Qt::AscendingOrder); - - /** - * Helper method to get the DolphinView::Sorting type for a given - * column \a column. If the column is smaller 0 or greater than the - * available columns, DolphinView::SortByName is returned. - */ - static DolphinView::Sorting sortingForColumn(int column); - -signals: - void sortingRoleChanged(); - -private: - DolphinView::Sorting m_sorting:16; - Qt::SortOrder m_sortOrder:16; -}; - -inline DolphinView::Sorting DolphinSortFilterProxyModel::sorting() const -{ - return m_sorting; -} - -inline Qt::SortOrder DolphinSortFilterProxyModel::sortOrder() const -{ - return m_sortOrder; -} - -#endif diff --git a/src/views/dolphintreeview.cpp b/src/views/dolphintreeview.cpp deleted file mode 100644 index 64b66aa84..000000000 --- a/src/views/dolphintreeview.cpp +++ /dev/null @@ -1,530 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2010 by Peter Penz <[email protected]> * - * Copyright (C) 2008 by Simon St. James <[email protected]> * - * * - * 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 "dolphintreeview.h" - -#include "dolphinmodel.h" - -#include <QApplication> -#include <QEvent> -#include <QHeaderView> -#include <QMouseEvent> -#include <QPainter> -#include <QScrollBar> - -DolphinTreeView::DolphinTreeView(QWidget* parent) : - QTreeView(parent), - m_expandingTogglePressed(false), - m_useDefaultIndexAt(true), - m_ignoreScrollTo(false), - m_dropRect(), - m_band() -{ - setUniformRowHeights(true); -} - -DolphinTreeView::~DolphinTreeView() -{ -} - -void DolphinTreeView::keyboardSearch(const QString & search) -{ - const QModelIndex oldCurrent = currentIndex(); - QTreeView::keyboardSearch(search); - if (currentIndex() != oldCurrent) { - // The current index has changed, but it is not selected yet. - // To select it, we call setCurrentIndex(...). - setCurrentIndex(currentIndex()); - } -} - -QRegion DolphinTreeView::visualRegionForSelection(const QItemSelection& selection) const -{ - // We have to make sure that the visualRect of each model index is inside the region. - // QTreeView::visualRegionForSelection does not do it right because it assumes implicitly - // that all visualRects have the same width, which is in general not the case here. - QRegion selectionRegion; - const QModelIndexList indexes = selection.indexes(); - - foreach(const QModelIndex& index, indexes) { - selectionRegion += visualRect(index); - } - - return selectionRegion; -} - -bool DolphinTreeView::acceptsDrop(const QModelIndex& index) const -{ - Q_UNUSED(index); - return false; -} - -bool DolphinTreeView::event(QEvent* event) -{ - switch (event->type()) { - case QEvent::Polish: - m_useDefaultIndexAt = false; - break; - default: - break; - } - return QTreeView::event(event); -} - -void DolphinTreeView::mousePressEvent(QMouseEvent* event) -{ - const QModelIndex current = currentIndex(); - QTreeView::mousePressEvent(event); - - m_expandingTogglePressed = isAboveExpandingToggle(event->pos()); - - const QModelIndex index = indexAt(event->pos()); - const bool updateState = index.isValid() && - (index.column() == DolphinModel::Name) && - (event->button() == Qt::LeftButton); - if (updateState) { - setState(QAbstractItemView::DraggingState); - } - - if (!index.isValid() || (index.column() != DolphinModel::Name)) { - const Qt::KeyboardModifiers mod = QApplication::keyboardModifiers(); - if (!m_expandingTogglePressed && !(mod & Qt::ShiftModifier) && !(mod & Qt::ControlModifier)) { - clearSelection(); - } - - // Restore the current index, other columns are handled as viewport area. - // setCurrentIndex(...) implicitly calls scrollTo(...), which we want to ignore. - m_ignoreScrollTo = true; - selectionModel()->setCurrentIndex(current, QItemSelectionModel::Current); - m_ignoreScrollTo = false; - - if ((event->button() == Qt::LeftButton) && !m_expandingTogglePressed) { - // Inform Qt about what we are doing - otherwise it starts dragging items around! - setState(DragSelectingState); - m_band.show = true; - // Incremental update data will not be useful - start from scratch. - m_band.ignoreOldInfo = true; - const QPoint scrollPos(horizontalScrollBar()->value(), verticalScrollBar()->value()); - m_band.origin = event->pos() + scrollPos; - m_band.destination = m_band.origin; - m_band.originalSelection = selectionModel()->selection(); - } - } -} - -void DolphinTreeView::mouseMoveEvent(QMouseEvent* event) -{ - if (m_expandingTogglePressed) { - // Per default QTreeView starts either a selection or a drag operation when dragging - // the expanding toggle button (Qt-issue - see TODO comment in DolphinIconsView::mousePressEvent()). - // Turn off this behavior in Dolphin to stay predictable: - setState(QAbstractItemView::NoState); - return; - } - - if (m_band.show) { - const QPoint mousePos = event->pos(); - const QModelIndex index = indexAt(mousePos); - if (!index.isValid()) { - // The destination of the selection rectangle is above the viewport. In this - // case QTreeView does no selection at all, which is not the wanted behavior - // in Dolphin. Select all items within the elastic band rectangle. - updateElasticBandSelection(); - } - - // TODO: Enable QTreeView::mouseMoveEvent(event) again, as soon - // as the Qt-issue #199631 has been fixed. - // QTreeView::mouseMoveEvent(event); - QAbstractItemView::mouseMoveEvent(event); - updateElasticBand(); - } else { - // TODO: Enable QTreeView::mouseMoveEvent(event) again, as soon - // as the Qt-issue #199631 has been fixed. - // QTreeView::mouseMoveEvent(event); - QAbstractItemView::mouseMoveEvent(event); - } -} - -void DolphinTreeView::mouseReleaseEvent(QMouseEvent* event) -{ - if (!m_expandingTogglePressed) { - const QModelIndex index = indexAt(event->pos()); - if (index.isValid() && (index.column() == DolphinModel::Name)) { - QTreeView::mouseReleaseEvent(event); - } else { - // don't change the current index if the cursor is released - // above any other column than the name column, as the other - // columns act as viewport - const QModelIndex current = currentIndex(); - QTreeView::mouseReleaseEvent(event); - selectionModel()->setCurrentIndex(current, QItemSelectionModel::Current); - } - } - m_expandingTogglePressed = false; - - if (m_band.show) { - setState(NoState); - updateElasticBand(); - m_band.show = false; - } -} - -void DolphinTreeView::startDrag(Qt::DropActions supportedActions) -{ - Q_UNUSED(supportedActions); - m_band.show = false; -} - -void DolphinTreeView::dragEnterEvent(QDragEnterEvent* event) -{ - Q_UNUSED(event); - if (m_band.show) { - updateElasticBand(); - m_band.show = false; - } -} - -void DolphinTreeView::dragMoveEvent(QDragMoveEvent* event) -{ - QTreeView::dragMoveEvent(event); - - setDirtyRegion(m_dropRect); - - const QModelIndex index = indexAt(event->pos()); - if (acceptsDrop(index)) { - m_dropRect = visualRect(index); - } else { - m_dropRect.setSize(QSize()); // set invalid - } - setDirtyRegion(m_dropRect); -} - -void DolphinTreeView::dragLeaveEvent(QDragLeaveEvent* event) -{ - QTreeView::dragLeaveEvent(event); - setDirtyRegion(m_dropRect); -} - -void DolphinTreeView::paintEvent(QPaintEvent* event) -{ - QTreeView::paintEvent(event); - if (m_band.show) { - // The following code has been taken from QListView - // and adapted to DolphinDetailsView. - // (C) 1992-2007 Trolltech ASA - QStyleOptionRubberBand opt; - opt.initFrom(this); - opt.shape = QRubberBand::Rectangle; - opt.opaque = false; - opt.rect = elasticBandRect(); - - QPainter painter(viewport()); - painter.save(); - style()->drawControl(QStyle::CE_RubberBand, &opt, &painter); - painter.restore(); - } -} - -QModelIndex DolphinTreeView::indexAt(const QPoint& point) const -{ - // The blank portion of the name column counts as empty space - const QModelIndex index = QTreeView::indexAt(point); - const bool isAboveEmptySpace = !m_useDefaultIndexAt && - (index.column() == KDirModel::Name) && - !visualRect(index).contains(point); - return isAboveEmptySpace ? QModelIndex() : index; -} - -void DolphinTreeView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command) -{ - // We must override setSelection() as Qt calls it internally and when this happens - // we must ensure that the default indexAt() is used. - if (!m_band.show) { - m_useDefaultIndexAt = true; - QTreeView::setSelection(rect, command); - m_useDefaultIndexAt = false; - } else { - // Use our own elastic band selection algorithm - updateElasticBandSelection(); - } -} - - -void DolphinTreeView::scrollTo(const QModelIndex & index, ScrollHint hint) -{ - if (!m_ignoreScrollTo) { - QTreeView::scrollTo(index, hint); - } -} - -void DolphinTreeView::updateElasticBandSelection() -{ - if (!m_band.show) { - return; - } - - // Ensure the elastic band itself is up-to-date, in - // case we are being called due to e.g. a drag event. - updateElasticBand(); - - // Clip horizontally to the name column, as some filenames will be - // longer than the column. We don't clip vertically as origin - // may be above or below the current viewport area. - const int nameColumnX = header()->sectionPosition(DolphinModel::Name); - const int nameColumnWidth = header()->sectionSize(DolphinModel::Name); - QRect selRect = elasticBandRect().normalized(); - QRect nameColumnArea(nameColumnX, selRect.y(), nameColumnWidth, selRect.height()); - selRect = nameColumnArea.intersect(selRect).normalized(); - // Get the last elastic band rectangle, expressed in viewpoint coordinates. - const QPoint scrollPos(horizontalScrollBar()->value(), verticalScrollBar()->value()); - QRect oldSelRect = QRect(m_band.lastSelectionOrigin - scrollPos, m_band.lastSelectionDestination - scrollPos).normalized(); - - if (selRect.isNull()) { - selectionModel()->select(m_band.originalSelection, QItemSelectionModel::ClearAndSelect); - m_band.ignoreOldInfo = true; - return; - } - - if (!m_band.ignoreOldInfo) { - // Do some quick checks to see if we can rule out the need to - // update the selection. - Q_ASSERT(uniformRowHeights()); - QModelIndex dummyIndex = model()->index(0, 0); - if (!dummyIndex.isValid()) { - // No items in the model presumably. - return; - } - - // If the elastic band does not cover the same rows as before, we'll - // need to re-check, and also invalidate the old item distances. - const int rowHeight = QTreeView::rowHeight(dummyIndex); - const bool coveringSameRows = - (selRect.top() / rowHeight == oldSelRect.top() / rowHeight) && - (selRect.bottom() / rowHeight == oldSelRect.bottom() / rowHeight); - if (coveringSameRows) { - // Covering the same rows, but have we moved far enough horizontally - // that we might have (de)selected some other items? - const bool itemSelectionChanged = - ((selRect.left() > oldSelRect.left()) && - (selRect.left() > m_band.insideNearestLeftEdge)) || - ((selRect.left() < oldSelRect.left()) && - (selRect.left() <= m_band.outsideNearestLeftEdge)) || - ((selRect.right() < oldSelRect.right()) && - (selRect.left() >= m_band.insideNearestRightEdge)) || - ((selRect.right() > oldSelRect.right()) && - (selRect.right() >= m_band.outsideNearestRightEdge)); - - if (!itemSelectionChanged) { - return; - } - } - } else { - // This is the only piece of optimization data that needs to be explicitly - // discarded. - m_band.lastSelectionOrigin = QPoint(); - m_band.lastSelectionDestination = QPoint(); - oldSelRect = selRect; - } - - // Do the selection from scratch. Force a update of the horizontal distances info. - m_band.insideNearestLeftEdge = nameColumnX + nameColumnWidth + 1; - m_band.insideNearestRightEdge = nameColumnX - 1; - m_band.outsideNearestLeftEdge = nameColumnX - 1; - m_band.outsideNearestRightEdge = nameColumnX + nameColumnWidth + 1; - - // Include the old selection rect as well, so we can deselect - // items that were inside it but not in the new selRect. - const QRect boundingRect = selRect.united(oldSelRect).normalized(); - if (boundingRect.isNull()) { - return; - } - - // Get the index of the item in this row in the name column. - // TODO - would this still work if the columns could be re-ordered? - QModelIndex startIndex = QTreeView::indexAt(boundingRect.topLeft()); - if (startIndex.parent().isValid()) { - startIndex = startIndex.parent().child(startIndex.row(), KDirModel::Name); - } else { - startIndex = model()->index(startIndex.row(), KDirModel::Name); - } - if (!startIndex.isValid()) { - selectionModel()->select(m_band.originalSelection, QItemSelectionModel::ClearAndSelect); - m_band.ignoreOldInfo = true; - return; - } - - // Go through all indexes between the top and bottom of boundingRect, and - // update the selection. - const int verticalCutoff = boundingRect.bottom(); - QModelIndex currIndex = startIndex; - QModelIndex lastIndex; - bool allItemsInBoundDone = false; - - // Calling selectionModel()->select(...) for each item that needs to be - // toggled is slow as each call emits selectionChanged(...) so store them - // and do the selection toggle in one batch. - QItemSelection itemsToToggle; - // QItemSelection's deal with continuous ranges of indexes better than - // single indexes, so try to portion items that need to be toggled into ranges. - bool formingToggleIndexRange = false; - QModelIndex toggleIndexRangeBegin = QModelIndex(); - - do { - QRect currIndexRect = visualRect(currIndex); - - // Update some optimization info as we go. - const int cr = currIndexRect.right(); - const int cl = currIndexRect.left(); - const int sl = selRect.left(); - const int sr = selRect.right(); - // "The right edge of the name is outside of the rect but nearer than m_outsideNearestLeft", etc - if ((cr < sl && cr > m_band.outsideNearestLeftEdge)) { - m_band.outsideNearestLeftEdge = cr; - } - if ((cl > sr && cl < m_band.outsideNearestRightEdge)) { - m_band.outsideNearestRightEdge = cl; - } - if ((cl >= sl && cl <= sr && cl > m_band.insideNearestRightEdge)) { - m_band.insideNearestRightEdge = cl; - } - if ((cr >= sl && cr <= sr && cr < m_band.insideNearestLeftEdge)) { - m_band.insideNearestLeftEdge = cr; - } - - bool currentlySelected = selectionModel()->isSelected(currIndex); - bool originallySelected = m_band.originalSelection.contains(currIndex); - bool intersectsSelectedRect = currIndexRect.intersects(selRect); - bool shouldBeSelected = (intersectsSelectedRect && !originallySelected) || (!intersectsSelectedRect && originallySelected); - bool needToToggleItem = (currentlySelected && !shouldBeSelected) || (!currentlySelected && shouldBeSelected); - if (needToToggleItem && !formingToggleIndexRange) { - toggleIndexRangeBegin = currIndex; - formingToggleIndexRange = true; - } - - // NOTE: indexBelow actually walks up and down expanded trees for us. - QModelIndex nextIndex = indexBelow(currIndex); - allItemsInBoundDone = !nextIndex.isValid() || currIndexRect.top() > verticalCutoff; - - const bool commitToggleIndexRange = formingToggleIndexRange && - (!needToToggleItem || - allItemsInBoundDone || - currIndex.parent() != toggleIndexRangeBegin.parent()); - if (commitToggleIndexRange) { - formingToggleIndexRange = false; - // If this is the last item in the bounds and it is also the beginning of a range, - // don't toggle lastIndex - it will already have been dealt with. - if (!allItemsInBoundDone || toggleIndexRangeBegin != currIndex) { - itemsToToggle.select(toggleIndexRangeBegin, lastIndex); - } - // Need to start a new range immediately with currIndex? - if (needToToggleItem) { - toggleIndexRangeBegin = currIndex; - formingToggleIndexRange = true; - } - if (allItemsInBoundDone && needToToggleItem) { - // Toggle the very last item in the bounds. - itemsToToggle.select(currIndex, currIndex); - } - } - - // Next item - lastIndex = currIndex; - currIndex = nextIndex; - } while (!allItemsInBoundDone); - - - selectionModel()->select(itemsToToggle, QItemSelectionModel::Toggle); - - m_band.lastSelectionOrigin = m_band.origin; - m_band.lastSelectionDestination = m_band.destination; - m_band.ignoreOldInfo = false; -} - -void DolphinTreeView::updateElasticBand() -{ - if (m_band.show) { - QRect dirtyRegion(elasticBandRect()); - const QPoint scrollPos(horizontalScrollBar()->value(), verticalScrollBar()->value()); - m_band.destination = viewport()->mapFromGlobal(QCursor::pos()) + scrollPos; - // Going above the (logical) top-left of the view causes complications during selection; - // we may as well prevent it. - if (m_band.destination.y() < 0) { - m_band.destination.setY(0); - } - if (m_band.destination.x() < 0) { - m_band.destination.setX(0); - } - dirtyRegion = dirtyRegion.united(elasticBandRect()); - setDirtyRegion(dirtyRegion); - } -} - -QRect DolphinTreeView::elasticBandRect() const -{ - const QPoint scrollPos(horizontalScrollBar()->value(), verticalScrollBar()->value()); - - const QPoint topLeft = m_band.origin - scrollPos; - const QPoint bottomRight = m_band.destination - scrollPos; - return QRect(topLeft, bottomRight).normalized(); -} - -bool DolphinTreeView::isAboveExpandingToggle(const QPoint& pos) const -{ - // QTreeView offers no public API to get the information whether an index has an - // expanding toggle and what boundaries the toggle has. The following approach - // also assumes a toggle for file items. - if (itemsExpandable()) { - const QModelIndex index = QTreeView::indexAt(pos); - if (index.isValid() && (index.column() == KDirModel::Name)) { - QRect rect = visualRect(index); - const int toggleSize = rect.height(); - if (isRightToLeft()) { - rect.moveRight(rect.right()); - } else { - rect.moveLeft(rect.x() - toggleSize); - } - rect.setWidth(toggleSize); - - QStyleOption opt; - opt.initFrom(this); - opt.rect = rect; - rect = style()->subElementRect(QStyle::SE_TreeViewDisclosureItem, &opt, this); - - return rect.contains(pos); - } - } - return false; -} - -DolphinTreeView::ElasticBand::ElasticBand() : - show(false), - origin(), - destination(), - lastSelectionOrigin(), - lastSelectionDestination(), - ignoreOldInfo(true), - outsideNearestLeftEdge(0), - outsideNearestRightEdge(0), - insideNearestLeftEdge(0), - insideNearestRightEdge(0) -{ -} - -#include "dolphintreeview.moc" diff --git a/src/views/dolphintreeview.h b/src/views/dolphintreeview.h deleted file mode 100644 index c037d412a..000000000 --- a/src/views/dolphintreeview.h +++ /dev/null @@ -1,134 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2010 by Peter Penz <[email protected]> * - * Copyright (C) 2008 by Simon St. James <[email protected]> * - * * - * 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 DOLPHINTREEVIEW_H -#define DOLPHINTREEVIEW_H - -#include <QTreeView> -#include <libdolphin_export.h> - -/** - * @brief Extends QTreeView by a custom selection- and hover-mechanism. - * - * QTreeView does not respect the width of a cell for the hover-feedback - * and when selecting items. DolphinTreeView improves this by respecting the - * content-width of the first column. The selection-mechanism also - * respects the content-width. - */ -class LIBDOLPHINPRIVATE_EXPORT DolphinTreeView : public QTreeView -{ - Q_OBJECT - -public: - explicit DolphinTreeView(QWidget* parent = 0); - virtual ~DolphinTreeView(); - - virtual QModelIndex indexAt (const QPoint& point) const; - virtual void keyboardSearch(const QString & search); - virtual QRegion visualRegionForSelection(const QItemSelection& selection) const; - -protected: - /** - * @return True, if the item with the index \p index accepts a drop. In this - * case a visual feedback for the user is given during dragging. Per - * default false is returned. - */ - virtual bool acceptsDrop(const QModelIndex& index) const; - - virtual bool event(QEvent* event); - virtual void mousePressEvent(QMouseEvent* event); - virtual void mouseMoveEvent(QMouseEvent* event); - virtual void mouseReleaseEvent(QMouseEvent* event); - virtual void startDrag(Qt::DropActions supportedActions); - virtual void dragEnterEvent(QDragEnterEvent* event); - virtual void dragMoveEvent(QDragMoveEvent* event); - virtual void dragLeaveEvent(QDragLeaveEvent* event); - virtual void paintEvent(QPaintEvent* event); - virtual void setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags command); - virtual void scrollTo(const QModelIndex& index, ScrollHint hint = EnsureVisible); - -private slots: - /** - * If the elastic band is currently shown, update the elastic band based on - * the current mouse position and ensure that the selection is the set of items - * intersecting it. - */ - void updateElasticBandSelection(); - - /** - * Updates the destination \a destination from - * the elastic band to the current mouse position and triggers - * an update. - */ - void updateElasticBand(); - - /** - * Returns the rectangle for the elastic band dependent from the - * origin \a origin, the current destination - * \a destination and the viewport position. - */ - QRect elasticBandRect() const; - -private: - /** - * Returns true, if \a pos is within the expanding toggle of a tree. - */ - bool isAboveExpandingToggle(const QPoint& pos) const; - -private: - bool m_expandingTogglePressed; - bool m_useDefaultIndexAt; // true, if QTreeView::indexAt() should be used - bool m_ignoreScrollTo; // true if calls to scrollTo(...) should do nothing. - - QRect m_dropRect; - - struct ElasticBand - { - ElasticBand(); - - // Elastic band origin and destination coordinates are relative to t - // he origin of the view, not the viewport. - bool show; - QPoint origin; - QPoint destination; - - // Optimization mechanisms for use with elastic band selection. - // Basically, allow "incremental" updates to the selection based - // on the last elastic band shape. - QPoint lastSelectionOrigin; - QPoint lastSelectionDestination; - - // If true, compute the set of selected elements from scratch (slower) - bool ignoreOldInfo; - - // Edges of the filenames that are closest to the edges of oldSelectionRect. - // Used to decide whether horizontal changes in the elastic band are likely - // to require us to re-check which items are selected. - int outsideNearestLeftEdge; - int outsideNearestRightEdge; - int insideNearestLeftEdge; - int insideNearestRightEdge; - // The set of items that were selected at the time this band was shown. - // NOTE: Unless CTRL was pressed when the band was created, this is always empty. - QItemSelection originalSelection; - } m_band; -}; - -#endif diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index 681ce74f7..bcc682876 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -36,6 +36,10 @@ #include <KFileItem> #include <KFileItemListProperties> #include <KLocale> +#include <kitemviews/kfileitemmodel.h> +#include <kitemviews/kfileitemlistview.h> +#include <kitemviews/kitemlistview.h> +#include <kitemviews/kitemlistcontroller.h> #include <KIO/DeleteJob> #include <KIO/NetAccess> #include <KIO/PreviewJob> @@ -50,39 +54,36 @@ #include "additionalinfoaccessor.h" #include "dolphindirlister.h" -#include "dolphinmodel.h" -#include "dolphincolumnviewcontainer.h" -#include "dolphinviewcontroller.h" -#include "dolphindetailsview.h" -#include "dolphinfileitemdelegate.h" #include "dolphinnewfilemenuobserver.h" -#include "dolphinsortfilterproxymodel.h" #include "dolphin_detailsmodesettings.h" -#include "dolphiniconsview.h" #include "dolphin_generalsettings.h" -#include "draganddrophelper.h" #include "renamedialog.h" #include "settings/dolphinsettings.h" #include "viewmodecontroller.h" #include "viewproperties.h" #include "zoomlevelinfo.h" -#include "dolphindetailsviewexpander.h" + +// TODO: +#include "dolphinitemlistcontainer.h" + +namespace { + const int MaxModeEnum = DolphinView::CompactView; + const int MaxSortingEnum = DolphinView::SortByPath; +}; DolphinView::DolphinView(const KUrl& url, QWidget* parent) : QWidget(parent), m_active(true), - m_showPreview(false), - m_storedCategorizedSorting(false), m_tabsForFiles(false), - m_isContextMenuOpen(false), m_assureVisibleCurrentIndex(false), m_expanderActive(false), m_isFolderWritable(true), + m_url(url), m_mode(DolphinView::IconsView), + m_additionalInfoList(), m_topLayout(0), - m_dolphinViewController(0), - m_viewModeController(0), - m_viewAccessor(), + m_dirLister(0), + m_container(0), m_selectionChangedTimer(0), m_activeItemUrl(), m_restoredContentsPosition(), @@ -94,12 +95,12 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) : m_topLayout->setSpacing(0); m_topLayout->setMargin(0); - m_dolphinViewController = new DolphinViewController(this); + //m_dolphinViewController = new DolphinViewController(this); - m_viewModeController = new ViewModeController(this); - m_viewModeController->setUrl(url); + //m_viewModeController = new ViewModeController(this); + //m_viewModeController->setUrl(url); - connect(m_viewModeController, SIGNAL(urlChanged(const KUrl&)), + /*connect(m_viewModeController, SIGNAL(urlChanged(const KUrl&)), this, SIGNAL(urlChanged(const KUrl&))); connect(m_dolphinViewController, SIGNAL(requestContextMenu(const QPoint&, const QList<QAction*>&)), @@ -112,20 +113,20 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) : this, SLOT(updateSortOrder(Qt::SortOrder))); connect(m_dolphinViewController, SIGNAL(sortFoldersFirstChanged(bool)), this, SLOT(updateSortFoldersFirst(bool))); - connect(m_dolphinViewController, SIGNAL(additionalInfoChanged(const KFileItemDelegate::InformationList&)), - this, SLOT(updateAdditionalInfo(const KFileItemDelegate::InformationList&))); - connect(m_dolphinViewController, SIGNAL(itemTriggered(const KFileItem&)), - this, SLOT(triggerItem(const KFileItem&))); - connect(m_dolphinViewController, SIGNAL(tabRequested(const KUrl&)), - this, SIGNAL(tabRequested(const KUrl&))); - connect(m_dolphinViewController, SIGNAL(activated()), + connect(m_dolphinViewController, SIGNAL(additionalInfoChanged(const QList<DolphinView::AdditionalInfo>&)), + this, SLOT(updateAdditionalInfo(const QList<DolphinView::AdditionalInfo>&)));*/ + //connect(m_dolphinViewController, SIGNAL(itemTriggered(const KFileItem&)), + // this, SLOT(triggerItem(const KFileItem&))); + //connect(m_dolphinViewController, SIGNAL(tabRequested(const KUrl&)), + // this, SIGNAL(tabRequested(const KUrl&))); + /*connect(m_dolphinViewController, SIGNAL(activated()), this, SLOT(activate())); connect(m_dolphinViewController, SIGNAL(itemEntered(const KFileItem&)), this, SLOT(showHoverInformation(const KFileItem&))); connect(m_dolphinViewController, SIGNAL(viewportEntered()), this, SLOT(clearHoverInformation())); connect(m_dolphinViewController, SIGNAL(urlChangeRequested(KUrl)), - this, SLOT(slotUrlChangeRequested(KUrl))); + this, SLOT(slotUrlChangeRequested(KUrl)));*/ // When a new item has been created by the "Create New..." menu, the item should // get selected and it must be assured that the item will get visible. As the @@ -139,8 +140,38 @@ DolphinView::DolphinView(const KUrl& url, QWidget* parent) : connect(m_selectionChangedTimer, SIGNAL(timeout()), this, SLOT(emitSelectionChangedSignal())); + m_dirLister = new DolphinDirLister(this); + m_dirLister->setAutoUpdate(true); + m_dirLister->setDelayedMimeTypes(true); + + connect(m_dirLister, SIGNAL(redirection(KUrl,KUrl)), this, SLOT(slotRedirection(KUrl,KUrl))); + connect(m_dirLister, SIGNAL(started(KUrl)), this, SLOT(slotDirListerStarted(KUrl))); + connect(m_dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted())); + connect(m_dirLister, SIGNAL(refreshItems(const QList<QPair<KFileItem,KFileItem>>&)), + this, SLOT(slotRefreshItems())); + + connect(m_dirLister, SIGNAL(clear()), this, SIGNAL(itemCountChanged())); + connect(m_dirLister, SIGNAL(newItems(KFileItemList)), this, SIGNAL(itemCountChanged())); + connect(m_dirLister, SIGNAL(infoMessage(const QString&)), this, SIGNAL(infoMessage(const QString&))); + connect(m_dirLister, SIGNAL(errorMessage(const QString&)), this, SIGNAL(infoMessage(const QString&))); + connect(m_dirLister, SIGNAL(percent(int)), this, SIGNAL(pathLoadingProgress(int))); + connect(m_dirLister, SIGNAL(urlIsFileError(const KUrl&)), this, SIGNAL(urlIsFileError(const KUrl&))); + connect(m_dirLister, SIGNAL(itemsDeleted(const KFileItemList&)), this, SIGNAL(itemCountChanged())); + + m_container = new DolphinItemListContainer(m_dirLister, this); + QHash<QByteArray, int> visibleRoles; + visibleRoles.insert("name", 0); + m_container->setVisibleRoles(visibleRoles); + + KItemListController* controller = m_container->controller(); + connect(controller, SIGNAL(itemClicked(int, Qt::MouseButton)), + this, SLOT(slotItemClicked(int, Qt::MouseButton))); + connect(controller, SIGNAL(itemExpansionToggleClicked(int)), this, SLOT(slotItemExpansionToggleClicked(int))); + applyViewProperties(); - m_topLayout->addWidget(m_viewAccessor.layoutTarget()); + m_topLayout->addWidget(m_container); + + loadDirectory(url); } DolphinView::~DolphinView() @@ -149,17 +180,7 @@ DolphinView::~DolphinView() KUrl DolphinView::url() const { - return m_viewModeController->url(); -} - -KUrl DolphinView::rootUrl() const -{ - const KUrl viewUrl = url(); - const KUrl root = m_viewAccessor.rootUrl(); - if (root.isEmpty() || !root.isParentOf(viewUrl)) { - return viewUrl; - } - return root; + return m_url; } void DolphinView::setActive(bool active) @@ -175,21 +196,26 @@ void DolphinView::setActive(bool active) color.setAlpha(150); } - QWidget* viewport = m_viewAccessor.itemView()->viewport(); - QPalette palette; - palette.setColor(viewport->backgroundRole(), color); - viewport->setPalette(palette); + /*QAbstractItemView* view = m_viewAccessor.itemView(); + QWidget* viewport = view ? view->viewport() : 0; + if (viewport) { + QPalette palette; + palette.setColor(viewport->backgroundRole(), color); + viewport->setPalette(palette); + }*/ update(); if (active) { - m_viewAccessor.itemView()->setFocus(); + //if (view) { + // view->setFocus(); + //} emit activated(); emitSelectionChangedSignal(); emit writeStateChanged(m_isFolderWritable); } - m_viewModeController->indicateActivationChange(active); + //m_viewModeController->indicateActivationChange(active); } bool DolphinView::isActive() const @@ -199,49 +225,13 @@ bool DolphinView::isActive() const void DolphinView::setMode(Mode mode) { - if (mode == m_mode) { - return; // the wished mode is already set - } + if (mode != m_mode) { + ViewProperties props(url()); + props.setViewMode(mode); + props.save(); - const int oldZoomLevel = m_viewModeController->zoomLevel(); - m_mode = mode; - - // remember the currently selected items, so that they will - // be restored after reloading the directory - m_selectedItems = selectedItems(); - - const bool hasFocus = m_viewAccessor.itemView()->hasFocus(); - deleteView(); - - const KUrl viewPropsUrl = rootUrl(); - ViewProperties props(viewPropsUrl); - props.setViewMode(m_mode); - createView(); - - if (hasFocus) { - m_viewAccessor.itemView()->setFocus(); - } - - // the file item delegate has been recreated, apply the current - // additional information manually - const KFileItemDelegate::InformationList infoList = props.additionalInfo(); - m_viewAccessor.itemDelegate()->setShowInformation(infoList); - emit additionalInfoChanged(); - - // Not all view modes support categorized sorting. Adjust the sorting model - // if changing the view mode results in a change of the categorized sorting - // capabilities. - m_storedCategorizedSorting = props.categorizedSorting(); - const bool categorized = m_storedCategorizedSorting && supportsCategorizedSorting(); - if (categorized != m_viewAccessor.proxyModel()->isCategorizedModel()) { - m_viewAccessor.proxyModel()->setCategorizedModel(categorized); - emit categorizedSortingChanged(); + applyViewProperties(); } - - emit modeChanged(); - - updateZoomLevel(oldZoomLevel); - loadDirectory(viewPropsUrl); } DolphinView::Mode DolphinView::mode() const @@ -249,46 +239,30 @@ DolphinView::Mode DolphinView::mode() const return m_mode; } -bool DolphinView::showPreview() const +bool DolphinView::previewsShown() const { - return m_showPreview; + return m_container->previewsShown(); } -bool DolphinView::showHiddenFiles() const +bool DolphinView::hiddenFilesShown() const { - return m_viewAccessor.dirLister()->showingDotFiles(); + return m_dirLister->showingDotFiles(); } bool DolphinView::categorizedSorting() const { - // If all view modes would support categorized sorting, returning - // m_viewAccessor.proxyModel()->isCategorizedModel() would be the way to go. As - // currently only the icons view supports caterized sorting, we remember - // the stored view properties state in m_storedCategorizedSorting and - // return this state. The application takes care to disable the corresponding - // checkbox by checking DolphinView::supportsCategorizedSorting() to indicate - // that this setting is not applied to the current view mode. - return m_storedCategorizedSorting; -} - -bool DolphinView::supportsCategorizedSorting() const -{ - return m_viewAccessor.supportsCategorizedSorting(); -} - -KFileItem DolphinView::rootItem() const -{ - return m_viewAccessor.dirLister()->rootItem(); + return false; //m_storedCategorizedSorting; } KFileItemList DolphinView::items() const { - return m_viewAccessor.dirLister()->items(); + return m_dirLister->items(); } KFileItemList DolphinView::selectedItems() const { - KFileItemList itemList; + return KFileItemList(); +/* KFileItemList itemList; const QAbstractItemView* view = m_viewAccessor.itemView(); if (!view) { return itemList; @@ -304,17 +278,18 @@ KFileItemList DolphinView::selectedItems() const } } - return itemList; + return itemList;*/ } int DolphinView::selectedItemsCount() const { - const QAbstractItemView* view = m_viewAccessor.itemView(); + return 0; + /*const QAbstractItemView* view = m_viewAccessor.itemView(); if (!view) { return 0; } - return view->selectionModel()->selectedIndexes().count(); + return view->selectionModel()->selectedIndexes().count();*/ } void DolphinView::markUrlsAsSelected(const QList<KUrl>& urls) @@ -327,30 +302,27 @@ void DolphinView::markUrlsAsSelected(const QList<KUrl>& urls) void DolphinView::setItemSelectionEnabled(const QRegExp& pattern, bool enabled) { - const QItemSelection matchingIndexes = childrenMatchingPattern(QModelIndex(), pattern); + Q_UNUSED(pattern); + Q_UNUSED(enabled); + /*const QItemSelection matchingIndexes = childrenMatchingPattern(QModelIndex(), pattern); const QItemSelectionModel::SelectionFlags command = enabled ? QItemSelectionModel::Select : QItemSelectionModel::Deselect; - m_viewAccessor.itemView()->selectionModel()->select(matchingIndexes, command); + m_viewAccessor.itemView()->selectionModel()->select(matchingIndexes, command);*/ } void DolphinView::setZoomLevel(int level) { - if (level < ZoomLevelInfo::minimumLevel()) { - level = ZoomLevelInfo::minimumLevel(); - } else if (level > ZoomLevelInfo::maximumLevel()) { - level = ZoomLevelInfo::maximumLevel(); - } - - if (level != zoomLevel()) { - m_viewModeController->setZoomLevel(level); - emit zoomLevelChanged(level); + const int oldZoomLevel = zoomLevel(); + m_container->setZoomLevel(level); + if (zoomLevel() != oldZoomLevel) { + emit zoomLevelChanged(zoomLevel(), oldZoomLevel); } } int DolphinView::zoomLevel() const { - return m_viewModeController->zoomLevel(); + return m_container->zoomLevel(); } void DolphinView::setSorting(Sorting sorting) @@ -362,7 +334,8 @@ void DolphinView::setSorting(Sorting sorting) DolphinView::Sorting DolphinView::sorting() const { - return m_viewAccessor.proxyModel()->sorting(); + return DolphinView::SortByName; + //return m_viewAccessor.proxyModel()->sorting(); } void DolphinView::setSortOrder(Qt::SortOrder order) @@ -374,7 +347,7 @@ void DolphinView::setSortOrder(Qt::SortOrder order) Qt::SortOrder DolphinView::sortOrder() const { - return m_viewAccessor.proxyModel()->sortOrder(); + return Qt::AscendingOrder; // m_viewAccessor.proxyModel()->sortOrder(); } void DolphinView::setSortFoldersFirst(bool foldersFirst) @@ -386,26 +359,25 @@ void DolphinView::setSortFoldersFirst(bool foldersFirst) bool DolphinView::sortFoldersFirst() const { - return m_viewAccessor.proxyModel()->sortFoldersFirst(); + return true; // m_viewAccessor.proxyModel()->sortFoldersFirst(); } -void DolphinView::setAdditionalInfo(KFileItemDelegate::InformationList info) +void DolphinView::setAdditionalInfoList(const QList<AdditionalInfo>& info) { - const KUrl viewPropsUrl = rootUrl(); - ViewProperties props(viewPropsUrl); - props.setAdditionalInfo(info); - m_viewAccessor.itemDelegate()->setShowInformation(info); + const QList<AdditionalInfo> previousList = info; - emit additionalInfoChanged(); + ViewProperties props(url()); + props.setAdditionalInfoList(info); - if (m_viewAccessor.reloadOnAdditionalInfoChange()) { - loadDirectory(viewPropsUrl); - } + m_additionalInfoList = info; + applyAdditionalInfoListToView(); + + emit additionalInfoListChanged(m_additionalInfoList, previousList); } -KFileItemDelegate::InformationList DolphinView::additionalInfo() const +QList<DolphinView::AdditionalInfo> DolphinView::additionalInfoList() const { - return m_viewAccessor.itemDelegate()->showInformation(); + return m_additionalInfoList; } void DolphinView::reload() @@ -424,16 +396,15 @@ void DolphinView::reload() void DolphinView::stopLoading() { - m_viewAccessor.dirLister()->stop(); + m_dirLister->stop(); } void DolphinView::refresh() { const bool oldActivationState = m_active; - const int oldZoomLevel = m_viewModeController->zoomLevel(); + const int oldZoomLevel = zoomLevel(); m_active = true; - createView(); applyViewProperties(); reload(); @@ -443,19 +414,20 @@ void DolphinView::refresh() void DolphinView::setNameFilter(const QString& nameFilter) { - m_viewModeController->setNameFilter(nameFilter); + Q_UNUSED(nameFilter); + //m_viewModeController->setNameFilter(nameFilter); } QString DolphinView::nameFilter() const { - return m_viewModeController->nameFilter(); + return QString(); //m_viewModeController->nameFilter(); } void DolphinView::calculateItemCount(int& fileCount, int& folderCount, KIO::filesize_t& totalFileSize) const { - foreach (const KFileItem& item, m_viewAccessor.dirLister()->items()) { + foreach (const KFileItem& item, m_dirLister->items()) { if (item.isDir()) { ++folderCount; } else { @@ -526,34 +498,31 @@ QString DolphinView::statusBarText() const QList<QAction*> DolphinView::versionControlActions(const KFileItemList& items) const { - return m_dolphinViewController->versionControlActions(items); + Q_UNUSED(items); + return QList<QAction*>(); //m_dolphinViewController->versionControlActions(items); } void DolphinView::setUrl(const KUrl& url) { - if (m_viewModeController->url() == url) { + if (url == m_url) { return; } - const bool hadSelection = hasSelection(); - - // The selection model and directory lister might change in the case of the column view: - disconnectViewAccessor(); + emit urlAboutToBeChanged(url); + const bool hadSelection = hasSelection(); m_newFileNames.clear(); + m_url = url; - m_viewModeController->setUrl(url); // emits urlChanged, which we forward - m_viewAccessor.prepareUrlChange(url); + // It is important to clear the items from the model before + // applying the view properties, otherwise expensive operations + // might be done on the existing items although they get cleared + // anyhow afterwards by loadDirectory(). + fileItemModel()->clear(); applyViewProperties(); - - // When changing the URL there is no need to keep the version - // data of the previous URL. - m_viewAccessor.dirModel()->clearVersionData(); - - // Reconnect to the (probably) new selection model and directory lister - connectViewAccessor(); loadDirectory(url); + emit urlChanged(url); if (hadSelection || hasSelection()) { emitSelectionChangedSignal(); } @@ -561,12 +530,12 @@ void DolphinView::setUrl(const KUrl& url) void DolphinView::selectAll() { - m_viewAccessor.itemView()->selectAll(); + //m_viewAccessor.itemView()->selectAll(); } void DolphinView::invertSelection() { - // Implementation note: Using selectionModel->select(selection, QItemSelectionModel::Toggle) does not +/* // Implementation note: Using selectionModel->select(selection, QItemSelectionModel::Toggle) does not // work, as QItemSelectionModel::hasSelection() provides invalid values in this case. This might be a Qt-issue - // when changing the implementation with an updated Qt-version don't forget to run the Dolphin-unit-tests that // verify this usecase. @@ -583,11 +552,12 @@ void DolphinView::invertSelection() QItemSelectionModel* selectionModel = m_viewAccessor.itemView()->selectionModel(); selectionModel->select(invertedSelection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current); + */ } void DolphinView::clearSelection() { - m_viewAccessor.itemView()->clearSelection(); + //m_viewAccessor.itemView()->clearSelection(); } void DolphinView::renameSelectedItems() @@ -598,17 +568,17 @@ void DolphinView::renameSelectedItems() return; } - if ((itemCount == 1) && DolphinSettings::instance().generalSettings()->renameInline()) { + /*if ((itemCount == 1) && DolphinSettings::instance().generalSettings()->renameInline()) { const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForItem(items.first()); const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->mapFromSource(dirIndex); m_viewAccessor.itemView()->edit(proxyIndex); - } else { + } else {*/ RenameDialog* dialog = new RenameDialog(this, items); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); dialog->raise(); dialog->activateWindow(); - } + //} // assure that the current index remains visible when KDirLister // will notify the view about changed items @@ -662,40 +632,33 @@ void DolphinView::pasteIntoFolder() } } -void DolphinView::setShowPreview(bool show) +void DolphinView::setPreviewsShown(bool show) { - if (m_showPreview == show) { + if (previewsShown() == show) { return; } - const KUrl viewPropsUrl = rootUrl(); - ViewProperties props(viewPropsUrl); - props.setShowPreview(show); - - m_showPreview = show; - const int oldZoomLevel = m_viewModeController->zoomLevel(); - emit showPreviewChanged(); + ViewProperties props(url()); + props.setPreviewsShown(show); - // Enabling or disabling the preview might change the icon size of the view. - // As the view does not emit a signal when the icon size has been changed, - // the used zoom level of the controller must be adjusted manually: - updateZoomLevel(oldZoomLevel); + m_container->setPreviewsShown(show); + emit previewsShownChanged(show); } -void DolphinView::setShowHiddenFiles(bool show) +void DolphinView::setHiddenFilesShown(bool show) { - if (m_viewAccessor.dirLister()->showingDotFiles() == show) { + if (m_dirLister->showingDotFiles() == show) { return; } m_selectedItems = selectedItems(); - const KUrl viewPropsUrl = rootUrl(); - ViewProperties props(viewPropsUrl); - props.setShowHiddenFiles(show); + ViewProperties props(url()); + props.setHiddenFilesShown(show); - m_viewAccessor.dirLister()->setShowingDotFiles(show); - emit showHiddenFilesChanged(); + m_dirLister->setShowingDotFiles(show); + m_dirLister->emitChanges(); + emit hiddenFilesShownChanged(show); } void DolphinView::setCategorizedSorting(bool categorized) @@ -704,18 +667,13 @@ void DolphinView::setCategorizedSorting(bool categorized) return; } - // setCategorizedSorting(true) may only get invoked - // if the view supports categorized sorting - Q_ASSERT(!categorized || supportsCategorizedSorting()); - - ViewProperties props(rootUrl()); + ViewProperties props(url()); props.setCategorizedSorting(categorized); props.save(); - m_storedCategorizedSorting = categorized; - m_viewAccessor.proxyModel()->setCategorizedModel(categorized); + //m_viewAccessor.proxyModel()->setCategorizedModel(categorized); - emit categorizedSortingChanged(); + emit categorizedSortingChanged(categorized); } void DolphinView::mouseReleaseEvent(QMouseEvent* event) @@ -724,61 +682,16 @@ void DolphinView::mouseReleaseEvent(QMouseEvent* event) setActive(true); } -bool DolphinView::eventFilter(QObject* watched, QEvent* event) +void DolphinView::contextMenuEvent(QContextMenuEvent* event) { - switch (event->type()) { - case QEvent::FocusIn: - if (watched == m_viewAccessor.itemView()) { - m_dolphinViewController->requestActivation(); - } - break; - - case QEvent::DragEnter: - if (watched == m_viewAccessor.itemView()->viewport()) { - setActive(true); - } - break; - - case QEvent::KeyPress: - if (watched == m_viewAccessor.itemView()) { - // clear the selection when Escape has been pressed - QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); - if (keyEvent->key() == Qt::Key_Escape) { - clearSelection(); - } - } - break; - - case QEvent::Wheel: - if (watched == m_viewAccessor.itemView()->viewport()) { - // Ctrl+wheel events should cause icon zooming, but not if the left mouse button is pressed - // (the user is probably trying to scroll during a selection in that case) - QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event); - if (wheelEvent->modifiers() & Qt::ControlModifier && !(wheelEvent->buttons() & Qt::LeftButton)) { - const int delta = wheelEvent->delta(); - const int level = zoomLevel(); - if (delta > 0) { - setZoomLevel(level + 1); - } else if (delta < 0) { - setZoomLevel(level - 1); - } - return true; - } - } - break; + Q_UNUSED(event); - default: - break; - } - - return QWidget::eventFilter(watched, event); -} - -void DolphinView::showEvent(QShowEvent* event) -{ - QWidget::showEvent(event); - if (!event->spontaneous()) { - loadDirectory(url()); + const QPoint pos = m_container->mapFromGlobal(QCursor::pos()); + const KItemListView* view = m_container->controller()->view(); + if (view->itemAt(pos) < 0) { + // Only open the context-menu if the cursor is above the viewport + // (the context-menu for items is handled in slotItemClicked()) + requestContextMenu(KFileItem(), url(), QList<QAction*>()); } } @@ -787,21 +700,27 @@ void DolphinView::activate() setActive(true); } -void DolphinView::triggerItem(const KFileItem& item) +void DolphinView::slotItemClicked(int index, Qt::MouseButton button) { - const Qt::KeyboardModifiers modifier = QApplication::keyboardModifiers(); - if ((modifier & Qt::ShiftModifier) || (modifier & Qt::ControlModifier)) { - // items are selected by the user, hence don't trigger the - // item specified by 'index' - return; - } + const KFileItem item = fileItemModel()->fileItem(index); - // TODO: the m_isContextMenuOpen check is a workaround for Qt-issue 207192 - if (item.isNull() || m_isContextMenuOpen) { - return; + if (button & Qt::LeftButton) { + emit itemTriggered(item); // caught by DolphinViewContainer or DolphinPart + } else if (button & Qt::MidButton) { + if (item.isDir() || isTabsForFilesEnabled()) { + emit tabRequested(item.url()); + } + } else if (button & Qt::RightButton) { + // TODO: attach customActions for the details-view + emit requestContextMenu(item, url(), QList<QAction*>()); } +} - emit itemTriggered(item); // caught by DolphinViewContainer or DolphinPart +void DolphinView::slotItemExpansionToggleClicked(int index) +{ + KFileItemModel* model = fileItemModel(); + const bool expanded = model->isExpanded(index); + model->setExpanded(index, !expanded); } void DolphinView::slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) @@ -826,86 +745,63 @@ void DolphinView::emitSelectionChangedSignal() void DolphinView::openContextMenu(const QPoint& pos, const QList<QAction*>& customActions) { + Q_UNUSED(pos); KFileItem item; - const QModelIndex index = m_viewAccessor.itemView()->indexAt(pos); + /*QAbstractItemView* view = m_viewAccessor.itemView(); + QModelIndex index; + if (view) { + index = view->indexAt(pos); + } + if (index.isValid() && (index.column() == DolphinModel::Name)) { const QModelIndex dolphinModelIndex = m_viewAccessor.proxyModel()->mapToSource(index); item = m_viewAccessor.dirModel()->itemForIndex(dolphinModelIndex); - } + }*/ - m_isContextMenuOpen = true; // TODO: workaround for Qt-issue 207192 emit requestContextMenu(item, url(), customActions); - m_isContextMenuOpen = false; } void DolphinView::dropUrls(const KFileItem& destItem, const KUrl& destPath, QDropEvent* event) { + Q_UNUSED(destItem); + Q_UNUSED(destPath); addNewFileNames(event->mimeData()); - DragAndDropHelper::instance().dropUrls(destItem, destPath, event, this); + //DragAndDropHelper::instance().dropUrls(destItem, destPath, event, this); } void DolphinView::updateSorting(DolphinView::Sorting sorting) { - ViewProperties props(rootUrl()); + ViewProperties props(url()); props.setSorting(sorting); - m_viewAccessor.proxyModel()->setSorting(sorting); + KItemModelBase* model = m_container->controller()->model(); + model->setSortRole(sortRoleForSorting(sorting)); emit sortingChanged(sorting); } void DolphinView::updateSortOrder(Qt::SortOrder order) { - ViewProperties props(rootUrl()); + ViewProperties props(url()); props.setSortOrder(order); - m_viewAccessor.proxyModel()->setSortOrder(order); + //m_viewAccessor.proxyModel()->setSortOrder(order); emit sortOrderChanged(order); } void DolphinView::updateSortFoldersFirst(bool foldersFirst) { - ViewProperties props(rootUrl()); + ViewProperties props(url()); props.setSortFoldersFirst(foldersFirst); - m_viewAccessor.proxyModel()->setSortFoldersFirst(foldersFirst); + //m_viewAccessor.proxyModel()->setSortFoldersFirst(foldersFirst); emit sortFoldersFirstChanged(foldersFirst); } -void DolphinView::updateAdditionalInfo(const KFileItemDelegate::InformationList& info) -{ - ViewProperties props(rootUrl()); - props.setAdditionalInfo(info); - props.save(); - - m_viewAccessor.itemDelegate()->setShowInformation(info); - - emit additionalInfoChanged(); -} - -void DolphinView::updateAdditionalInfoActions(KActionCollection* collection) -{ - const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); - - const KFileItemDelegate::InformationList checkedInfo = m_viewAccessor.itemDelegate()->showInformation(); - const KFileItemDelegate::InformationList infoKeys = infoAccessor.keys(); - - const bool enable = (m_mode == DolphinView::DetailsView) || - (m_mode == DolphinView::IconsView); - - foreach (const KFileItemDelegate::Information& info, infoKeys) { - const QString name = infoAccessor.actionCollectionName(info, AdditionalInfoAccessor::AdditionalInfoType); - QAction* action = collection->action(name); - Q_ASSERT(action); - action->setEnabled(enable); - action->setChecked(checkedInfo.contains(info)); - } -} - QPair<bool, QString> DolphinView::pasteInfo() const { return KonqOperations::pasteInfo(url()); @@ -923,7 +819,7 @@ bool DolphinView::isTabsForFilesEnabled() const bool DolphinView::itemsExpandable() const { - return m_viewAccessor.itemsExpandable(); + return false; //m_viewAccessor.itemsExpandable(); } void DolphinView::restoreState(QDataStream& stream) @@ -931,81 +827,75 @@ void DolphinView::restoreState(QDataStream& stream) // Restore the URL of the current item that had the keyboard focus stream >> m_activeItemUrl; - // Restore the root URL - KUrl rootUrl; - stream >> rootUrl; - m_viewAccessor.setRootUrl(rootUrl); - // Restore the view position stream >> m_restoredContentsPosition; // Restore expanded folders (only relevant for the details view - will be ignored by the view in other view modes) QSet<KUrl> urlsToExpand; stream >> urlsToExpand; - const DolphinDetailsViewExpander* expander = m_viewAccessor.setExpandedUrls(urlsToExpand); + /*const DolphinDetailsViewExpander* expander = m_viewAccessor.setExpandedUrls(urlsToExpand); if (expander) { m_expanderActive = true; connect (expander, SIGNAL(completed()), this, SLOT(slotLoadingCompleted())); } else { m_expanderActive = false; - } + }*/ } void DolphinView::saveState(QDataStream& stream) { // Save the URL of the current item that has the keyboard focus - KFileItem currentItem; - const QAbstractItemView* view = m_viewAccessor.itemView(); - - if (view) { - const QModelIndex proxyIndex = view->currentIndex(); - const QModelIndex dirModelIndex = m_viewAccessor.proxyModel()->mapToSource(proxyIndex); - currentItem = m_viewAccessor.dirModel()->itemForIndex(dirModelIndex); - } KUrl currentItemUrl; - if (!currentItem.isNull()) { - currentItemUrl = currentItem.url(); - } + //if (!currentItem.isNull()) { + // currentItemUrl = currentItem.url(); + //} stream << currentItemUrl; - // Save the root URL - stream << m_viewAccessor.rootUrl(); - // Save view position - const int x = view->horizontalScrollBar()->value(); - const int y = view->verticalScrollBar()->value(); + const qreal x = m_container->horizontalScrollBar()->value(); + const qreal y = m_container->verticalScrollBar()->value(); stream << QPoint(x, y); + kDebug() << "saving view state" << QPoint(x, y); // Save expanded folders (only relevant for the details view - the set will be empty in other view modes) - stream << m_viewAccessor.expandedUrls(); + //stream << m_viewAccessor.expandedUrls(); } bool DolphinView::hasSelection() const { - const QAbstractItemView* view = m_viewAccessor.itemView(); - return view && view->selectionModel()->hasSelection(); + //const QAbstractItemView* view = m_viewAccessor.itemView(); + //return view && view->selectionModel()->hasSelection(); + return false; +} + +KFileItem DolphinView::rootItem() const +{ + return m_dirLister->rootItem(); } void DolphinView::observeCreatedItem(const KUrl& url) { m_createdItemUrl = url; - connect(m_viewAccessor.dirModel(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), - this, SLOT(selectAndScrollToCreatedItem())); + //connect(m_dirModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), + // this, SLOT(selectAndScrollToCreatedItem())); } void DolphinView::selectAndScrollToCreatedItem() { - const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForUrl(m_createdItemUrl); + /*const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForUrl(m_createdItemUrl); if (dirIndex.isValid()) { const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->mapFromSource(dirIndex); - m_viewAccessor.itemView()->setCurrentIndex(proxyIndex); + QAbstractItemView* view = m_viewAccessor.itemView(); + if (view) { + view->setCurrentIndex(proxyIndex); + } } disconnect(m_viewAccessor.dirModel(), SIGNAL(rowsInserted(const QModelIndex&, int, int)), - this, SLOT(selectAndScrollToCreatedItem())); + this, SLOT(selectAndScrollToCreatedItem()));*/ m_createdItemUrl = KUrl(); } @@ -1013,7 +903,7 @@ void DolphinView::slotRedirection(const KUrl& oldUrl, const KUrl& newUrl) { if (oldUrl.equals(url(), KUrl::CompareWithoutTrailingSlash)) { emit redirection(oldUrl, newUrl); - m_viewModeController->redirectToUrl(newUrl); // #186947 + m_url = newUrl; // #186947 } } @@ -1024,18 +914,16 @@ void DolphinView::restoreContentsPosition() const int y = m_restoredContentsPosition.y(); m_restoredContentsPosition = QPoint(); - QAbstractItemView* view = m_viewAccessor.itemView(); - Q_ASSERT(view); - view->horizontalScrollBar()->setValue(x); - view->verticalScrollBar()->setValue(y); + m_container->horizontalScrollBar()->setValue(x); + m_container->verticalScrollBar()->setValue(y); } } -void DolphinView::slotUrlChangeRequested(const KUrl& url) +/*void DolphinView::slotUrlChangeRequested(const KUrl& url) { m_viewModeController->setUrl(url); updateWritableState(); -} +}*/ void DolphinView::showHoverInformation(const KFileItem& item) { @@ -1077,18 +965,21 @@ void DolphinView::slotDirListerCompleted() if (!m_newFileNames.isEmpty()) { // select all newly added items created by a paste operation or // a drag & drop operation, and clear the previous selection - m_viewAccessor.itemView()->clearSelection(); - const int rowCount = m_viewAccessor.proxyModel()->rowCount(); - QItemSelection selection; - for (int row = 0; row < rowCount; ++row) { - const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->index(row, 0); - const QModelIndex dirIndex = m_viewAccessor.proxyModel()->mapToSource(proxyIndex); - const KUrl url = m_viewAccessor.dirModel()->itemForIndex(dirIndex).url(); - if (m_newFileNames.contains(url.fileName())) { - selection.merge(QItemSelection(proxyIndex, proxyIndex), QItemSelectionModel::Select); + /*QAbstractItemView* view = m_viewAccessor.itemView(); + if (view) { + view->clearSelection(); + const int rowCount = m_viewAccessor.proxyModel()->rowCount(); + QItemSelection selection; + for (int row = 0; row < rowCount; ++row) { + const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->index(row, 0); + const QModelIndex dirIndex = m_viewAccessor.proxyModel()->mapToSource(proxyIndex); + const KUrl url = m_viewAccessor.dirModel()->itemForIndex(dirIndex).url(); + if (m_newFileNames.contains(url.fileName())) { + selection.merge(QItemSelection(proxyIndex, proxyIndex), QItemSelectionModel::Select); + } } - } - m_viewAccessor.itemView()->selectionModel()->select(selection, QItemSelectionModel::Select); + view->selectionModel()->select(selection, QItemSelectionModel::Select); + }*/ m_newFileNames.clear(); } @@ -1102,21 +993,23 @@ void DolphinView::slotLoadingCompleted() if (!m_activeItemUrl.isEmpty()) { // assure that the current item remains visible - const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForUrl(m_activeItemUrl); + /*const QModelIndex dirIndex = m_viewAccessor.dirModel()->indexForUrl(m_activeItemUrl); if (dirIndex.isValid()) { const QModelIndex proxyIndex = m_viewAccessor.proxyModel()->mapFromSource(dirIndex); QAbstractItemView* view = m_viewAccessor.itemView(); - const bool clearSelection = !hasSelection(); - view->setCurrentIndex(proxyIndex); - if (clearSelection) { - view->clearSelection(); + if (view) { + const bool clearSelection = !hasSelection(); + view->setCurrentIndex(proxyIndex); + if (clearSelection) { + view->clearSelection(); + } } m_activeItemUrl.clear(); - } + }*/ } if (!m_selectedItems.isEmpty()) { - const KUrl& baseUrl = url(); + /*const KUrl& baseUrl = url(); KUrl url; QItemSelection newSelection; foreach(const KFileItem& item, m_selectedItems) { @@ -1126,15 +1019,18 @@ void DolphinView::slotLoadingCompleted() newSelection.select(index, index); } } - m_viewAccessor.itemView()->selectionModel()->select(newSelection, + QAbstractItemView* view = m_viewAccessor.itemView(); + if (view) { + view->selectionModel()->select(newSelection, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Current); + }*/ m_selectedItems.clear(); } // Restore the contents position. This has to be done using a Qt::QueuedConnection // because the view might not be in its final state yet. - QMetaObject::invokeMethod(this, "restoreContentsPosition", Qt::QueuedConnection); + QTimer::singleShot(0, this, SLOT(restoreContentsPosition())); emit finishedPathLoading(url()); } @@ -1143,10 +1039,18 @@ void DolphinView::slotRefreshItems() { if (m_assureVisibleCurrentIndex) { m_assureVisibleCurrentIndex = false; - m_viewAccessor.itemView()->scrollTo(m_viewAccessor.itemView()->currentIndex()); + //QAbstractItemView* view = m_viewAccessor.itemView(); + //if (view) { + // m_viewAccessor.itemView()->scrollTo(m_viewAccessor.itemView()->currentIndex()); + //} } } +KFileItemModel* DolphinView::fileItemModel() const +{ + return static_cast<KFileItemModel*>(m_container->controller()->model()); +} + void DolphinView::loadDirectory(const KUrl& url, bool reload) { if (!url.isValid()) { @@ -1159,50 +1063,61 @@ void DolphinView::loadDirectory(const KUrl& url, bool reload) return; } - KDirLister* dirLister = m_viewAccessor.dirLister(); - dirLister->openUrl(url, reload ? KDirLister::Reload : KDirLister::NoFlags); + m_dirLister->openUrl(url, reload ? KDirLister::Reload : KDirLister::NoFlags); } void DolphinView::applyViewProperties() { - const ViewProperties props(rootUrl()); + m_container->beginTransaction(); + + const ViewProperties props(url()); const Mode mode = props.viewMode(); if (m_mode != mode) { - const int oldZoomLevel = m_viewModeController->zoomLevel(); - + const Mode previousMode = m_mode; m_mode = mode; - createView(); - emit modeChanged(); - updateZoomLevel(oldZoomLevel); - } - if (!m_viewAccessor.itemView()) { - createView(); - } + // Changing the mode might result in changing + // the zoom level. Remember the old zoom level so + // that zoomLevelChanged() can get emitted. + const int oldZoomLevel = m_container->zoomLevel(); + + switch (m_mode) { + case IconsView: m_container->setItemLayout(KFileItemListView::IconsLayout); break; + case CompactView: m_container->setItemLayout(KFileItemListView::CompactLayout); break; + case DetailsView: m_container->setItemLayout(KFileItemListView::DetailsLayout); break; + default: Q_ASSERT(false); break; + } + + emit modeChanged(m_mode, previousMode); - Q_ASSERT(m_viewAccessor.itemView()); - Q_ASSERT(m_viewAccessor.itemDelegate()); + if (m_container->zoomLevel() != oldZoomLevel) { + emit zoomLevelChanged(m_container->zoomLevel(), oldZoomLevel); + } + } - const bool showHiddenFiles = props.showHiddenFiles(); - if (showHiddenFiles != m_viewAccessor.dirLister()->showingDotFiles()) { - m_viewAccessor.dirLister()->setShowingDotFiles(showHiddenFiles); - emit showHiddenFilesChanged(); + const bool hiddenFilesShown = props.hiddenFilesShown(); + if (hiddenFilesShown != m_dirLister->showingDotFiles()) { + m_dirLister->setShowingDotFiles(hiddenFilesShown); + m_dirLister->emitChanges(); + emit hiddenFilesShownChanged(hiddenFilesShown); } - m_storedCategorizedSorting = props.categorizedSorting(); +/* m_storedCategorizedSorting = props.categorizedSorting(); const bool categorized = m_storedCategorizedSorting && supportsCategorizedSorting(); if (categorized != m_viewAccessor.proxyModel()->isCategorizedModel()) { m_viewAccessor.proxyModel()->setCategorizedModel(categorized); emit categorizedSortingChanged(); - } + }*/ const DolphinView::Sorting sorting = props.sorting(); - if (sorting != m_viewAccessor.proxyModel()->sorting()) { - m_viewAccessor.proxyModel()->setSorting(sorting); + KItemModelBase* model = m_container->controller()->model(); + const QByteArray newSortRole = sortRoleForSorting(sorting); + if (newSortRole != model->sortRole()) { + model->setSortRole(newSortRole); emit sortingChanged(sorting); } - +/* const Qt::SortOrder sortOrder = props.sortOrder(); if (sortOrder != m_viewAccessor.proxyModel()->sortOrder()) { m_viewAccessor.proxyModel()->setSortOrder(sortOrder); @@ -1214,72 +1129,45 @@ void DolphinView::applyViewProperties() m_viewAccessor.proxyModel()->setSortFoldersFirst(sortFoldersFirst); emit sortFoldersFirstChanged(sortFoldersFirst); } - - KFileItemDelegate::InformationList info = props.additionalInfo(); - if (info != m_viewAccessor.itemDelegate()->showInformation()) { - m_viewAccessor.itemDelegate()->setShowInformation(info); - emit additionalInfoChanged(); - } - - const bool showPreview = props.showPreview(); - if (showPreview != m_showPreview) { - m_showPreview = showPreview; - const int oldZoomLevel = m_viewModeController->zoomLevel(); - emit showPreviewChanged(); - - // Enabling or disabling the preview might change the icon size of the view. - // As the view does not emit a signal when the icon size has been changed, - // the used zoom level of the controller must be adjusted manually: - updateZoomLevel(oldZoomLevel); +*/ + const QList<DolphinView::AdditionalInfo> infoList = props.additionalInfoList(); + if (infoList != m_additionalInfoList) { + const QList<DolphinView::AdditionalInfo> previousList = m_additionalInfoList; + m_additionalInfoList = infoList; + applyAdditionalInfoListToView(); + emit additionalInfoListChanged(m_additionalInfoList, previousList); } -} - -void DolphinView::createView() -{ - deleteView(); - - Q_ASSERT(!m_viewAccessor.itemView()); - Q_ASSERT(!m_dolphinViewController->itemView()); - m_viewAccessor.createView(this, m_dolphinViewController, m_viewModeController, m_mode); - QAbstractItemView* view = m_viewAccessor.itemView(); - Q_ASSERT(view); - view->installEventFilter(this); - view->viewport()->installEventFilter(this); + const bool previewsShown = props.previewsShown(); + if (previewsShown != m_container->previewsShown()) { + const int oldZoomLevel = zoomLevel(); - m_dolphinViewController->setItemView(view); + m_container->setPreviewsShown(previewsShown); + emit previewsShownChanged(previewsShown); - const int zoomLevel = ZoomLevelInfo::zoomLevelForIconSize(view->iconSize()); - m_viewModeController->setZoomLevel(zoomLevel); - - connectViewAccessor(); + // Changing the preview-state might result in a changed zoom-level + if (oldZoomLevel != zoomLevel()) { + emit zoomLevelChanged(zoomLevel(), oldZoomLevel); + } + } - setFocusProxy(m_viewAccessor.layoutTarget()); - m_topLayout->insertWidget(1, m_viewAccessor.layoutTarget()); + m_container->endTransaction(); } -void DolphinView::deleteView() +void DolphinView::applyAdditionalInfoListToView() { - QAbstractItemView* view = m_viewAccessor.itemView(); - Q_ASSERT(!m_dolphinViewController->itemView() || (m_dolphinViewController->itemView() == view)); - m_dolphinViewController->setItemView(0); - - if (view) { - disconnectViewAccessor(); - - if (hasFocus()) { - // It's important to set the keyboard focus to the parent - // before deleting the view: Otherwise when having a split - // view the other view will get the focus and will request - // an activation (see DolphinView::eventFilter()). - setFocusProxy(0); - setFocus(); - } + const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); - m_viewModeController->disconnect(view); + QHash<QByteArray, int> visibleRoles; + visibleRoles.insert("name", 0); - m_viewAccessor.deleteView(); + int index = 1; + foreach (AdditionalInfo info, m_additionalInfoList) { + visibleRoles.insert(infoAccessor.role(info), index); + ++index; } + + m_container->setVisibleRoles(visibleRoles); } void DolphinView::pasteToUrl(const KUrl& url) @@ -1290,17 +1178,19 @@ void DolphinView::pasteToUrl(const KUrl& url) void DolphinView::updateZoomLevel(int oldZoomLevel) { - const int newZoomLevel = ZoomLevelInfo::zoomLevelForIconSize(m_viewAccessor.itemView()->iconSize()); + Q_UNUSED(oldZoomLevel); + /* const int newZoomLevel = ZoomLevelInfo::zoomLevelForIconSize(m_viewAccessor.itemView()->iconSize()); if (oldZoomLevel != newZoomLevel) { m_viewModeController->setZoomLevel(newZoomLevel); emit zoomLevelChanged(newZoomLevel); - } + }*/ } KUrl::List DolphinView::simplifiedSelectedUrls() const { + Q_ASSERT(false); // TODO KUrl::List urls; - +/* const KFileItemList items = selectedItems(); foreach (const KFileItem &item, items) { urls.append(item.url()); @@ -1309,17 +1199,18 @@ KUrl::List DolphinView::simplifiedSelectedUrls() const if (itemsExpandable()) { urls = KDirModel::simplifiedUrlList(urls); - } + }*/ return urls; } QMimeData* DolphinView::selectionMimeData() const { - const QAbstractItemView* view = m_viewAccessor.itemView(); + /*const QAbstractItemView* view = m_viewAccessor.itemView(); Q_ASSERT((view) && (view->selectionModel())); const QItemSelection selection = m_viewAccessor.proxyModel()->mapSelectionToSource(view->selectionModel()->selection()); - return m_viewAccessor.dirModel()->mimeData(selection.indexes()); + return m_viewAccessor.dirModel()->mimeData(selection.indexes());*/ + return 0; } void DolphinView::addNewFileNames(const QMimeData* mimeData) @@ -1332,8 +1223,10 @@ void DolphinView::addNewFileNames(const QMimeData* mimeData) QItemSelection DolphinView::childrenMatchingPattern(const QModelIndex& parent, const QRegExp& pattern) const { + Q_UNUSED(parent); + Q_UNUSED(pattern); QItemSelection matchingIndexes; - const DolphinSortFilterProxyModel* proxyModel = m_viewAccessor.proxyModel(); + /*const DolphinSortFilterProxyModel* proxyModel = m_viewAccessor.proxyModel(); const DolphinModel* dolphinModel = m_viewAccessor.dirModel(); const int rowCount = proxyModel->rowCount(parent); @@ -1349,61 +1242,17 @@ QItemSelection DolphinView::childrenMatchingPattern(const QModelIndex& parent, c if (proxyModel->hasChildren(index)) { matchingIndexes += childrenMatchingPattern(index, pattern); } - } + }*/ return matchingIndexes; } -void DolphinView::connectViewAccessor() -{ - KDirLister* dirLister = m_viewAccessor.dirLister(); - connect(dirLister, SIGNAL(redirection(KUrl,KUrl)), this, SLOT(slotRedirection(KUrl,KUrl))); - connect(dirLister, SIGNAL(started(KUrl)), this, SLOT(slotDirListerStarted(KUrl))); - connect(dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted())); - connect(dirLister, SIGNAL(refreshItems(const QList<QPair<KFileItem,KFileItem>>&)), - this, SLOT(slotRefreshItems())); - - connect(dirLister, SIGNAL(clear()), this, SIGNAL(itemCountChanged())); - connect(dirLister, SIGNAL(newItems(KFileItemList)), this, SIGNAL(itemCountChanged())); - connect(dirLister, SIGNAL(infoMessage(const QString&)), this, SIGNAL(infoMessage(const QString&))); - connect(dirLister, SIGNAL(errorMessage(const QString&)), this, SIGNAL(infoMessage(const QString&))); - connect(dirLister, SIGNAL(percent(int)), this, SIGNAL(pathLoadingProgress(int))); - connect(dirLister, SIGNAL(urlIsFileError(const KUrl&)), this, SIGNAL(urlIsFileError(const KUrl&))); - connect(dirLister, SIGNAL(itemsDeleted(const KFileItemList&)), this, SIGNAL(itemCountChanged())); - - QAbstractItemView* view = m_viewAccessor.itemView(); - connect(view->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), - this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection))); -} - -void DolphinView::disconnectViewAccessor() -{ - KDirLister* dirLister = m_viewAccessor.dirLister(); - disconnect(dirLister, SIGNAL(redirection(KUrl,KUrl)), this, SLOT(slotRedirection(KUrl,KUrl))); - disconnect(dirLister, SIGNAL(started(KUrl)), this, SLOT(slotDirListerStarted(KUrl))); - disconnect(dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted())); - disconnect(dirLister, SIGNAL(refreshItems(const QList<QPair<KFileItem,KFileItem>>&)), - this, SLOT(slotRefreshItems())); - - disconnect(dirLister, SIGNAL(clear()), this, SIGNAL(itemCountChanged())); - disconnect(dirLister, SIGNAL(newItems(KFileItemList)), this, SIGNAL(itemCountChanged())); - disconnect(dirLister, SIGNAL(infoMessage(const QString&)), this, SIGNAL(infoMessage(const QString&))); - disconnect(dirLister, SIGNAL(errorMessage(const QString&)), this, SIGNAL(errorMessage(const QString&))); - disconnect(dirLister, SIGNAL(percent(int)), this, SIGNAL(pathLoadingProgress(int))); - disconnect(dirLister, SIGNAL(urlIsFileError(const KUrl&)), this, SIGNAL(urlIsFileError(const KUrl&))); - disconnect(dirLister, SIGNAL(itemsDeleted(const KFileItemList&)), this, SIGNAL(itemCountChanged())); - - QAbstractItemView* view = m_viewAccessor.itemView(); - disconnect(view->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), - this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection))); -} - void DolphinView::updateWritableState() { const bool wasFolderWritable = m_isFolderWritable; m_isFolderWritable = true; - const KFileItem item = m_viewAccessor.dirLister()->rootItem(); + const KFileItem item; // = m_viewAccessor.dirLister()->rootItem(); if (!item.isNull()) { KFileItemListProperties capabilities(KFileItemList() << item); m_isFolderWritable = capabilities.supportsWriting(); @@ -1413,225 +1262,22 @@ void DolphinView::updateWritableState() } } -DolphinView::ViewAccessor::ViewAccessor() : - m_rootUrl(), - m_iconsView(0), - m_detailsView(0), - m_columnsContainer(0), - m_dolphinModel(0), - m_proxyModel(0), - m_dragSource(0) -{ - DolphinDirLister* dirLister = new DolphinDirLister(); - dirLister->setAutoUpdate(true); - dirLister->setDelayedMimeTypes(true); - - m_dolphinModel = new DolphinModel(); - m_dolphinModel->setDirLister(dirLister); // m_dolphinModel takes ownership of dirLister - m_dolphinModel->setDropsAllowed(DolphinModel::DropOnDirectory); - - m_proxyModel = new DolphinSortFilterProxyModel(); - m_proxyModel->setSourceModel(m_dolphinModel); - m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); -} - -DolphinView::ViewAccessor::~ViewAccessor() -{ - delete m_proxyModel; - m_proxyModel = 0; - - delete m_dolphinModel; - m_dolphinModel = 0; - - delete m_dragSource; - m_dragSource = 0; -} - -void DolphinView::ViewAccessor::createView(QWidget* parent, - DolphinViewController* dolphinViewController, - const ViewModeController* viewModeController, - Mode mode) -{ - Q_ASSERT(!itemView()); - - switch (mode) { - case IconsView: - m_iconsView = new DolphinIconsView(parent, - dolphinViewController, - viewModeController, - m_proxyModel); - break; - - case DetailsView: - m_detailsView = new DolphinDetailsView(parent, - dolphinViewController, - viewModeController, - m_proxyModel); - break; - - case ColumnView: - m_columnsContainer = new DolphinColumnViewContainer(parent, - dolphinViewController, - viewModeController); - if (!m_rootUrl.isEmpty() && m_rootUrl.isParentOf(viewModeController->url())) { - // The column-view must show several columns starting with m_rootUrl as - // first column and viewModeController->url() as last column. - m_columnsContainer->showColumn(m_rootUrl); - m_columnsContainer->showColumn(viewModeController->url()); - } - break; - - default: - Q_ASSERT(false); - } - - KDirLister* lister = dirLister(); - if (lister) { - lister->setMainWindow(parent->window()); - } -} - -void DolphinView::ViewAccessor::deleteView() -{ - if (m_columnsContainer) { - m_columnsContainer->close(); - m_columnsContainer->disconnect(); - m_columnsContainer->deleteLater(); - m_columnsContainer = 0; - } else { - QAbstractItemView* view = itemView(); - if (view) { - view->close(); - view->disconnect(); - - if (DragAndDropHelper::instance().isDragSource(view)) { - // The view 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; - } - view->hide(); - m_dragSource = view; - } else { - view->deleteLater(); - view = 0; - } - } - - m_iconsView = 0; - m_detailsView = 0; - } -} - -void DolphinView::ViewAccessor::prepareUrlChange(const KUrl& url) -{ - if (m_columnsContainer) { - m_columnsContainer->showColumn(url); - } -} - -QAbstractItemView* DolphinView::ViewAccessor::itemView() const -{ - if (m_iconsView) { - return m_iconsView; - } - - if (m_detailsView) { - return m_detailsView; - } - - if (m_columnsContainer) { - return m_columnsContainer->activeColumn(); - } - - return 0; -} - -KFileItemDelegate* DolphinView::ViewAccessor::itemDelegate() const -{ - return static_cast<KFileItemDelegate*>(itemView()->itemDelegate()); -} - -QWidget* DolphinView::ViewAccessor::layoutTarget() const -{ - if (m_columnsContainer) { - return m_columnsContainer; - } - return itemView(); -} - -void DolphinView::ViewAccessor::setRootUrl(const KUrl& rootUrl) -{ - m_rootUrl = rootUrl; -} - -KUrl DolphinView::ViewAccessor::rootUrl() const -{ - return m_columnsContainer ? m_columnsContainer->rootUrl() : m_rootUrl; -} - -bool DolphinView::ViewAccessor::supportsCategorizedSorting() const -{ - return m_iconsView != 0; -} - -bool DolphinView::ViewAccessor::itemsExpandable() const -{ - return m_detailsView && m_detailsView->itemsExpandable(); -} - -QSet<KUrl> DolphinView::ViewAccessor::expandedUrls() const -{ - if (m_detailsView) { - return m_detailsView->expandedUrls(); - } - - return QSet<KUrl>(); -} - -const DolphinDetailsViewExpander* DolphinView::ViewAccessor::setExpandedUrls(const QSet<KUrl>& urlsToExpand) -{ - if (m_detailsView && m_detailsView->itemsExpandable() && !urlsToExpand.isEmpty()) { - // Check if another expander is already active and stop it if necessary. - if(!m_detailsViewExpander.isNull()) { - m_detailsViewExpander->stop(); - } - - m_detailsViewExpander = new DolphinDetailsViewExpander(m_detailsView, urlsToExpand); - return m_detailsViewExpander; - } - else { - return 0; - } -} - -bool DolphinView::ViewAccessor::reloadOnAdditionalInfoChange() const +QByteArray DolphinView::sortRoleForSorting(Sorting sorting) const { - // the details view requires no reloading of the directory, as it maps - // the file item delegate info to its columns internally - return m_detailsView != 0; -} - -DolphinModel* DolphinView::ViewAccessor::dirModel() const -{ - return static_cast<DolphinModel*>(proxyModel()->sourceModel()); -} - -DolphinSortFilterProxyModel* DolphinView::ViewAccessor::proxyModel() const -{ - if (m_columnsContainer) { - return static_cast<DolphinSortFilterProxyModel*>(m_columnsContainer->activeColumn()->model()); + switch (sorting) { + case SortByName: return "name"; + case SortBySize: return "size"; + case SortByDate: return "date"; + case SortByPermissions: return "permissions"; + case SortByOwner: return "owner"; + case SortByGroup: return "group"; + case SortByType: return "type"; + case SortByDestination: return "destination"; + case SortByPath: return "path"; + default: break; } - return m_proxyModel; -} -KDirLister* DolphinView::ViewAccessor::dirLister() const -{ - return dirModel()->dirLister(); + return QByteArray(); } #include "dolphinview.moc" diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index 48967e62b..f5bc7d922 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -41,34 +41,25 @@ typedef KIO::FileUndoManager::CommandType CommandType; -class DolphinColumnViewContainer; -class DolphinDetailsView; -class DolphinDetailsViewExpander; -class DolphinIconsView; -class DolphinModel; -class DolphinSortFilterProxyModel; -class DolphinViewController; +class DolphinDirLister; +class DolphinItemListContainer; class KAction; class KActionCollection; -class KDirLister; +class KFileItemModel; class KUrl; -class ViewModeController; class ViewProperties; class QRegExp; /** * @short Represents a view for the directory content. * - * View modes for icons, details and columns are supported. It's + * View modes for icons, compact and details are supported. It's * possible to adjust: * - sort order * - sort type * - show hidden files * - show previews - * - * @see DolphinIconsView - * @see DolphinDetailsView - * @see DolphinColumnView + * - enable grouping */ class LIBDOLPHINPRIVATE_EXPORT DolphinView : public QWidget { @@ -76,31 +67,28 @@ class LIBDOLPHINPRIVATE_EXPORT DolphinView : public QWidget public: /** - * Defines the view mode for a directory. The view mode - * can be defined when constructing a DolphinView. The + * Defines the view mode for a directory. The * view mode is automatically updated if the directory itself * defines a view mode (see class ViewProperties for details). */ enum Mode { /** - * The directory items are shown as icons including an - * icon name. + * The items are shown as icons with a name-label below. */ IconsView = 0, /** - * The icon, the name and at least the size of the directory - * items are shown in a table. It is possible to add columns - * for date, group and permissions. + * The icon, the name and the size of the items are + * shown per default as a table. */ DetailsView = 1, /** - * Each folder is shown in a separate column. + * The items are shown as icons with the name-label aligned + * to the right side. */ - ColumnView = 2, - MaxModeEnum = ColumnView + CompactView = 3 }; /** Defines the sort order for the items of a directory. */ @@ -114,15 +102,29 @@ public: SortByGroup, SortByType, SortByDestination, - SortByPath, - MaxSortingEnum = SortByPath + SortByPath + }; + + /** Defines the additional information shown for the items of a directory. */ + enum AdditionalInfo + { + NoInfo = 0, + NameInfo, + SizeInfo, + DateInfo, + PermissionsInfo, + OwnerInfo, + GroupInfo, + TypeInfo, + DestinationInfo, + PathInfo }; /** * @param url Specifies the content which should be shown. * @param parent Parent widget of the view. */ - DolphinView( const KUrl& url, QWidget* parent); + DolphinView(const KUrl& url, QWidget* parent); virtual ~DolphinView(); @@ -133,17 +135,6 @@ public: KUrl url() const; /** - * Returns the root URL of the view, which is defined as the first - * visible path of DolphinView::url(). Usually the root URL is - * equal to DolphinView::url(), but in the case of the column view - * when 2 columns are shown, the root URL might be: - * /home/peter/Documents - * and DolphinView::url() might return - * /home/peter/Documents/Music/ - */ - KUrl rootUrl() const; - - /** * If \a active is true, the view will marked as active. The active * view is defined as view where all actions are applied to. */ @@ -159,31 +150,16 @@ public: void setMode(Mode mode); Mode mode() const; - /** See setShowPreview */ - bool showPreview() const; + /** See setPreviewsShown */ + bool previewsShown() const; /** See setShowHiddenFiles */ - bool showHiddenFiles() const; + bool hiddenFilesShown() const; /** See setCategorizedSorting */ bool categorizedSorting() const; /** - * Returns true, if the categorized sorting is supported by the current - * used mode (see DolphinView::setMode()). Currently only DolphinView::IconsView - * supports categorizations. To check whether the categorized - * sorting is set, use DolphinView::categorizedSorting(). - */ - bool supportsCategorizedSorting() const; - - /** - * Returns the root item which represents the current URL. Note that the returned - * item can be null (KFileItem::isNull() will return true) in case that the directory - * has not been loaded. - */ - KFileItem rootItem() const; - - /** * Returns the items of the view. */ KFileItemList items() const; @@ -253,10 +229,10 @@ public: bool sortFoldersFirst() const; /** Sets the additional information which should be shown for the items. */ - void setAdditionalInfo(KFileItemDelegate::InformationList info); + void setAdditionalInfoList(const QList<AdditionalInfo>& info); /** Returns the additional information which should be shown for the items. */ - KFileItemDelegate::InformationList additionalInfo() const; + QList<AdditionalInfo> additionalInfoList() const; /** Reloads the current directory. */ void reload(); @@ -300,11 +276,6 @@ public: QList<QAction*> versionControlActions(const KFileItemList& items) const; /** - * Updates the state of the 'Additional Information' actions in \a collection. - */ - void updateAdditionalInfoActions(KActionCollection* collection); - - /** * Returns the state of the paste action: * first is whether the action should be enabled * second is the text for the action @@ -339,6 +310,13 @@ public: /** Returns true, if at least one item is selected. */ bool hasSelection() const; + /** + * Returns the root item which represents the current URL. Note that the returned + * item can be null (KFileItem::isNull() will return true) in case that the directory + * has not been loaded. + */ + KFileItem rootItem() const; + public slots: /** * Changes the directory to \a url. If the current directory is equal to @@ -403,7 +381,7 @@ public slots: * (GeneralSettings::globalViewProps() returns false), then the * preview setting will be stored automatically. */ - void setShowPreview(bool show); + void setPreviewsShown(bool show); /** * Shows all hidden files of the current directory, @@ -412,7 +390,7 @@ public slots: * (GeneralSettings::globalViewProps() returns false), then the * show hidden file setting will be stored automatically. */ - void setShowHiddenFiles(bool show); + void setHiddenFilesShown(bool show); /** * Summarizes all sorted items by their category \a categorized @@ -429,6 +407,13 @@ signals: */ void activated(); + /** + * Is emitted if the URL of the view will be changed to \a url. + * After the URL has been changed the signal urlChanged() will + * be emitted. + */ + void urlAboutToBeChanged(const KUrl& url); + /** Is emitted if URL of the view has been changed to \a url. */ void urlChanged(const KUrl& url); @@ -451,16 +436,16 @@ signals: * Is emitted if the view mode (IconsView, DetailsView, * PreviewsView) has been changed. */ - void modeChanged(); + void modeChanged(Mode current, Mode previous); /** Is emitted if the 'show preview' property has been changed. */ - void showPreviewChanged(); + void previewsShownChanged(bool shown); /** Is emitted if the 'show hidden files' property has been changed. */ - void showHiddenFilesChanged(); + void hiddenFilesShownChanged(bool shown); /** Is emitted if the 'categorized sorting' property has been changed. */ - void categorizedSortingChanged(); + void categorizedSortingChanged(bool sortCategorized); /** Is emitted if the sorting by name, size or date has been changed. */ void sortingChanged(DolphinView::Sorting sorting); @@ -472,10 +457,11 @@ signals: void sortFoldersFirstChanged(bool foldersFirst); /** Is emitted if the additional information shown for this view has been changed. */ - void additionalInfoChanged(); + void additionalInfoListChanged(const QList<DolphinView::AdditionalInfo>& current, + const QList<DolphinView::AdditionalInfo>& previous); /** Is emitted if the zoom level has been changed by zooming in or out. */ - void zoomLevelChanged(int level); + void zoomLevelChanged(int current, int previous); /** * Is emitted if information of an item is requested to be shown e. g. in the panel. @@ -556,10 +542,8 @@ signals: void writeStateChanged(bool isFolderWritable); protected: - /** @see QWidget::mouseReleaseEvent */ virtual void mouseReleaseEvent(QMouseEvent* event); - virtual bool eventFilter(QObject* watched, QEvent* event); - virtual void showEvent(QShowEvent* event); + virtual void contextMenuEvent(QContextMenuEvent* event); private slots: /** @@ -568,12 +552,9 @@ private slots: */ void activate(); - /** - * If the item \a item is a directory, then this - * directory will be loaded. If the item is a file, the corresponding - * application will get started. - */ - void triggerItem(const KFileItem& index); + void slotItemClicked(int index, Qt::MouseButton button); + + void slotItemExpansionToggleClicked(int index); /** * Emits the signal \a selectionChanged() with a small delay. This is @@ -625,12 +606,6 @@ private slots: void updateSortFoldersFirst(bool foldersFirst); /** - * Updates the view properties of the current URL to the - * additional information given by \a info. - */ - void updateAdditionalInfo(const KFileItemDelegate::InformationList& info); - - /** * Updates the status bar to show hover information for the * item \a item. If currently other items are selected, * no hover information is shown. @@ -699,9 +674,11 @@ private slots: */ void restoreContentsPosition(); - void slotUrlChangeRequested(const KUrl& url); + //void slotUrlChangeRequested(const KUrl& url); private: + KFileItemModel* fileItemModel() const; + void loadDirectory(const KUrl& url, bool reload = false); /** @@ -710,13 +687,7 @@ private: */ void applyViewProperties(); - /** - * Creates a new view representing the given view mode (DolphinView::mode()). - * The current view will get deleted. - */ - void createView(); - - void deleteView(); + void applyAdditionalInfoListToView(); /** * Helper method for DolphinView::paste() and DolphinView::pasteIntoFolder(). @@ -758,9 +729,6 @@ private: */ QItemSelection childrenMatchingPattern(const QModelIndex& parent, const QRegExp& pattern) const; - void connectViewAccessor(); - void disconnectViewAccessor(); - /** * Updates m_isFolderWritable dependent on whether the folder represented by * the current URL is writable. If the state has changed, the signal @@ -768,88 +736,23 @@ private: */ void updateWritableState(); -private: - /** - * Abstracts the access to the different view implementations - * for icons-, details- and column-view. - */ - class ViewAccessor - { - public: - ViewAccessor(); - ~ViewAccessor(); - - void createView(QWidget* parent, - DolphinViewController* dolphinViewController, - const ViewModeController* viewModeController, - Mode mode); - void deleteView(); - - /** - * Must be invoked before the URL has been changed and allows view implementations - * like the column view to create a new column. - */ - void prepareUrlChange(const KUrl& url); - - QAbstractItemView* itemView() const; - KFileItemDelegate* itemDelegate() const; - - /** - * Returns the widget that should be added to the layout as target. Usually - * the item view itself is returned, but in the case of the column view - * a container widget is returned. - */ - QWidget* layoutTarget() const; - - void setRootUrl(const KUrl& rootUrl); - KUrl rootUrl() const; - - bool supportsCategorizedSorting() const; - bool itemsExpandable() const; - QSet<KUrl> expandedUrls() const; - const DolphinDetailsViewExpander* setExpandedUrls(const QSet<KUrl>& urlsToExpand); - - /** - * Returns true, if a reloading of the items is required - * when the additional information properties have been changed - * by the user. - */ - bool reloadOnAdditionalInfoChange() const; - - DolphinModel* dirModel() const; - DolphinSortFilterProxyModel* proxyModel() const; - KDirLister* dirLister() const; - - private: - KUrl m_rootUrl; - DolphinIconsView* m_iconsView; - DolphinDetailsView* m_detailsView; - DolphinColumnViewContainer* m_columnsContainer; - DolphinModel* m_dolphinModel; - DolphinSortFilterProxyModel* m_proxyModel; - QAbstractItemView* m_dragSource; - QPointer<DolphinDetailsViewExpander> m_detailsViewExpander; - - // For unit tests - friend class DolphinDetailsViewTest; - }; + QByteArray sortRoleForSorting(Sorting sorting) const; +private: bool m_active : 1; - bool m_showPreview : 1; - bool m_storedCategorizedSorting : 1; bool m_tabsForFiles : 1; - bool m_isContextMenuOpen : 1; // TODO: workaround for Qt-issue 207192 bool m_assureVisibleCurrentIndex : 1; bool m_expanderActive : 1; bool m_isFolderWritable : 1; + KUrl m_url; Mode m_mode; + QList<AdditionalInfo> m_additionalInfoList; QVBoxLayout* m_topLayout; - DolphinViewController* m_dolphinViewController; - ViewModeController* m_viewModeController; - ViewAccessor m_viewAccessor; + DolphinDirLister* m_dirLister; + DolphinItemListContainer* m_container; QTimer* m_selectionChangedTimer; diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp index 6046abc8c..87e828dfa 100644 --- a/src/views/dolphinviewactionhandler.cpp +++ b/src/views/dolphinviewactionhandler.cpp @@ -36,6 +36,8 @@ #include <KRun> #include <KPropertiesDialog> +#include <KDebug> + DolphinViewActionHandler::DolphinViewActionHandler(KActionCollection* collection, QObject* parent) : QObject(parent), m_actionCollection(collection), @@ -49,29 +51,32 @@ void DolphinViewActionHandler::setCurrentView(DolphinView* view) { Q_ASSERT(view); - if (m_currentView) + if (m_currentView) { disconnect(m_currentView, 0, this, 0); + } m_currentView = view; - connect(view, SIGNAL(modeChanged()), + connect(view, SIGNAL(modeChanged(DolphinView::Mode, DolphinView::Mode)), this, SLOT(updateViewActions())); - connect(view, SIGNAL(showPreviewChanged()), - this, SLOT(slotShowPreviewChanged())); + connect(view, SIGNAL(previewsShownChanged(bool)), + this, SLOT(slotPreviewsShownChanged(bool))); connect(view, SIGNAL(sortOrderChanged(Qt::SortOrder)), this, SLOT(slotSortOrderChanged(Qt::SortOrder))); connect(view, SIGNAL(sortFoldersFirstChanged(bool)), this, SLOT(slotSortFoldersFirstChanged(bool))); - connect(view, SIGNAL(additionalInfoChanged()), - this, SLOT(slotAdditionalInfoChanged())); - connect(view, SIGNAL(categorizedSortingChanged()), - this, SLOT(slotCategorizedSortingChanged())); - connect(view, SIGNAL(showHiddenFilesChanged()), - this, SLOT(slotShowHiddenFilesChanged())); + connect(view, SIGNAL(additionalInfoListChanged(QList<DolphinView::AdditionalInfo>, + QList<DolphinView::AdditionalInfo>)), + this, SLOT(slotAdditionalInfoListChanged(QList<DolphinView::AdditionalInfo>, + QList<DolphinView::AdditionalInfo>))); + connect(view, SIGNAL(categorizedSortingChanged(bool)), + this, SLOT(slotCategorizedSortingChanged(bool))); + connect(view, SIGNAL(hiddenFilesShownChanged(bool)), + this, SLOT(slotHiddenFilesShownChanged(bool))); connect(view, SIGNAL(sortingChanged(DolphinView::Sorting)), this, SLOT(slotSortingChanged(DolphinView::Sorting))); - connect(view, SIGNAL(zoomLevelChanged(int)), - this, SLOT(slotZoomLevelChanged(int))); + connect(view, SIGNAL(zoomLevelChanged(int, int)), + this, SLOT(slotZoomLevelChanged(int, int))); } DolphinView* DolphinViewActionHandler::currentView() @@ -130,14 +135,14 @@ void DolphinViewActionHandler::createActions() // View menu KToggleAction* iconsAction = iconsModeAction(); + KToggleAction* compactAction = compactModeAction(); KToggleAction* detailsAction = detailsModeAction(); - KToggleAction* columnsAction = columnsModeAction(); KSelectAction* viewModeActions = m_actionCollection->add<KSelectAction>("view_mode"); viewModeActions->setText(i18nc("@action:intoolbar", "View Mode")); viewModeActions->addAction(iconsAction); + viewModeActions->addAction(compactAction); viewModeActions->addAction(detailsAction); - viewModeActions->addAction(columnsAction); viewModeActions->setToolBarMode(KSelectAction::MenuMode); connect(viewModeActions, SIGNAL(triggered(QAction*)), this, SLOT(slotViewModeActionTriggered(QAction*))); @@ -214,8 +219,8 @@ QActionGroup* DolphinViewActionHandler::createAdditionalInformationActionGroup() const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); - const KFileItemDelegate::InformationList infoKeys = infoAccessor.keys(); - foreach (KFileItemDelegate::Information info, infoKeys) { + const QList<DolphinView::AdditionalInfo> infoList = infoAccessor.keys(); + foreach (DolphinView::AdditionalInfo info, infoList) { const QString name = infoAccessor.actionCollectionName(info, AdditionalInfoAccessor::AdditionalInfoType); KToggleAction* action = m_actionCollection->add<KToggleAction>(name); action->setText(infoAccessor.translation(info)); @@ -239,8 +244,8 @@ QActionGroup* DolphinViewActionHandler::createSortByActionGroup() sortByActionGroup->addAction(sortByName); const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); - const KFileItemDelegate::InformationList infoKeys = infoAccessor.keys(); - foreach (KFileItemDelegate::Information info, infoKeys) { + const QList<DolphinView::AdditionalInfo> infoList = infoAccessor.keys(); + foreach (DolphinView::AdditionalInfo info, infoList) { const QString name = infoAccessor.actionCollectionName(info, AdditionalInfoAccessor::SortByType); KToggleAction* action = m_actionCollection->add<KToggleAction>(name); action->setText(infoAccessor.translation(info)); @@ -289,11 +294,12 @@ void DolphinViewActionHandler::slotDeleteItems() void DolphinViewActionHandler::togglePreview(bool show) { emit actionBeingHandled(); - m_currentView->setShowPreview(show); + m_currentView->setPreviewsShown(show); } -void DolphinViewActionHandler::slotShowPreviewChanged() +void DolphinViewActionHandler::slotPreviewsShownChanged(bool shown) { + Q_UNUSED(shown); // It is not enough to update the 'Show Preview' action, also // the 'Zoom In' and 'Zoom Out' actions must be adapted. updateViewActions(); @@ -306,8 +312,8 @@ QString DolphinViewActionHandler::currentViewModeActionName() const return "icons"; case DolphinView::DetailsView: return "details"; - case DolphinView::ColumnView: - return "columns"; + case DolphinView::CompactView: + return "compact"; } return QString(); // can't happen } @@ -328,17 +334,17 @@ void DolphinViewActionHandler::updateViewActions() } QAction* showPreviewAction = m_actionCollection->action("show_preview"); - showPreviewAction->setChecked(m_currentView->showPreview()); + showPreviewAction->setChecked(m_currentView->previewsShown()); slotSortOrderChanged(m_currentView->sortOrder()); slotSortFoldersFirstChanged(m_currentView->sortFoldersFirst()); - slotAdditionalInfoChanged(); - slotCategorizedSortingChanged(); + slotAdditionalInfoListChanged(m_currentView->additionalInfoList(), QList<DolphinView::AdditionalInfo>()); + slotCategorizedSortingChanged(m_currentView->categorizedSorting()); slotSortingChanged(m_currentView->sorting()); - slotZoomLevelChanged(m_currentView->zoomLevel()); + slotZoomLevelChanged(m_currentView->zoomLevel(), -1); QAction* showHiddenFilesAction = m_actionCollection->action("show_hidden_files"); - showHiddenFilesAction->setChecked(m_currentView->showHiddenFiles()); + showHiddenFilesAction->setChecked(m_currentView->hiddenFilesShown()); } void DolphinViewActionHandler::zoomIn() @@ -385,10 +391,10 @@ void DolphinViewActionHandler::toggleAdditionalInfo(QAction* action) { emit actionBeingHandled(); - const KFileItemDelegate::Information info = - static_cast<KFileItemDelegate::Information>(action->data().toInt()); + const DolphinView::AdditionalInfo info = + static_cast<DolphinView::AdditionalInfo>(action->data().toInt()); - KFileItemDelegate::InformationList list = m_currentView->additionalInfo(); + QList<DolphinView::AdditionalInfo> list = m_currentView->additionalInfoList(); const bool show = action->isChecked(); @@ -396,17 +402,30 @@ void DolphinViewActionHandler::toggleAdditionalInfo(QAction* action) const bool containsInfo = (index >= 0); if (show && !containsInfo) { list.append(info); - m_currentView->setAdditionalInfo(list); + m_currentView->setAdditionalInfoList(list); } else if (!show && containsInfo) { list.removeAt(index); - m_currentView->setAdditionalInfo(list); + m_currentView->setAdditionalInfoList(list); Q_ASSERT(list.indexOf(info) < 0); } } -void DolphinViewActionHandler::slotAdditionalInfoChanged() +void DolphinViewActionHandler::slotAdditionalInfoListChanged(const QList<DolphinView::AdditionalInfo>& current, + const QList<DolphinView::AdditionalInfo>& previous) { - m_currentView->updateAdditionalInfoActions(m_actionCollection); + Q_UNUSED(previous); + + const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); + + const QList<DolphinView::AdditionalInfo> checkedInfo = current; + const QList<DolphinView::AdditionalInfo> infoList = infoAccessor.keys(); + + foreach (DolphinView::AdditionalInfo info, infoList) { + const QString name = infoAccessor.actionCollectionName(info, AdditionalInfoAccessor::AdditionalInfoType); + QAction* action = m_actionCollection->action(name); + Q_ASSERT(action); + action->setChecked(checkedInfo.contains(info)); + } } void DolphinViewActionHandler::toggleSortCategorization(bool categorizedSorting) @@ -414,23 +433,22 @@ void DolphinViewActionHandler::toggleSortCategorization(bool categorizedSorting) m_currentView->setCategorizedSorting(categorizedSorting); } -void DolphinViewActionHandler::slotCategorizedSortingChanged() +void DolphinViewActionHandler::slotCategorizedSortingChanged(bool sortCategorized) { QAction* showInGroupsAction = m_actionCollection->action("show_in_groups"); - showInGroupsAction->setChecked(m_currentView->categorizedSorting()); - showInGroupsAction->setEnabled(m_currentView->supportsCategorizedSorting()); + showInGroupsAction->setChecked(sortCategorized); } void DolphinViewActionHandler::toggleShowHiddenFiles(bool show) { emit actionBeingHandled(); - m_currentView->setShowHiddenFiles(show); + m_currentView->setHiddenFilesShown(show); } -void DolphinViewActionHandler::slotShowHiddenFilesChanged() +void DolphinViewActionHandler::slotHiddenFilesShownChanged(bool shown) { QAction* showHiddenFilesAction = m_actionCollection->action("show_hidden_files"); - showHiddenFilesAction->setChecked(m_currentView->showHiddenFiles()); + showHiddenFilesAction->setChecked(shown); } @@ -445,28 +463,28 @@ KToggleAction* DolphinViewActionHandler::iconsModeAction() return iconsView; } +KToggleAction* DolphinViewActionHandler::compactModeAction() +{ + KToggleAction* iconsView = m_actionCollection->add<KToggleAction>("compact"); + iconsView->setText(i18nc("@action:inmenu View Mode", "Compact")); + iconsView->setToolTip(i18nc("@info", "Compact view mode")); + iconsView->setShortcut(Qt::CTRL | Qt::Key_2); + iconsView->setIcon(KIcon("view-list-details")); // TODO: discuss with Oxygen-team the wrong (?) name + iconsView->setData(QVariant::fromValue(DolphinView::CompactView)); + return iconsView; +} + KToggleAction* DolphinViewActionHandler::detailsModeAction() { KToggleAction* detailsView = m_actionCollection->add<KToggleAction>("details"); detailsView->setText(i18nc("@action:inmenu View Mode", "Details")); detailsView->setToolTip(i18nc("@info", "Details view mode")); - detailsView->setShortcut(Qt::CTRL | Qt::Key_2); - detailsView->setIcon(KIcon("view-list-details")); + detailsView->setShortcut(Qt::CTRL | Qt::Key_3); + detailsView->setIcon(KIcon("view-list-text")); detailsView->setData(QVariant::fromValue(DolphinView::DetailsView)); return detailsView; } -KToggleAction* DolphinViewActionHandler::columnsModeAction() -{ - KToggleAction* columnView = m_actionCollection->add<KToggleAction>("columns"); - columnView->setText(i18nc("@action:inmenu View Mode", "Columns")); - columnView->setToolTip(i18nc("@info", "Columns view mode")); - columnView->setShortcut(Qt::CTRL | Qt::Key_3); - columnView->setIcon(KIcon("view-file-columns")); - columnView->setData(QVariant::fromValue(DolphinView::ColumnView)); - return columnView; -} - void DolphinViewActionHandler::slotSortingChanged(DolphinView::Sorting sorting) { QAction* action = 0; @@ -474,8 +492,8 @@ void DolphinViewActionHandler::slotSortingChanged(DolphinView::Sorting sorting) action = m_actionCollection->action("sort_by_name"); } else { const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); - const KFileItemDelegate::InformationList infoKeys = infoAccessor.keys(); - foreach (const KFileItemDelegate::Information info, infoKeys) { + const QList<DolphinView::AdditionalInfo> infoList = infoAccessor.keys(); + foreach (DolphinView::AdditionalInfo info, infoList) { if (sorting == infoAccessor.sorting(info)) { const QString name = infoAccessor.actionCollectionName(info, AdditionalInfoAccessor::SortByType); action = m_actionCollection->action(name); @@ -492,16 +510,18 @@ void DolphinViewActionHandler::slotSortingChanged(DolphinView::Sorting sorting) } } -void DolphinViewActionHandler::slotZoomLevelChanged(int level) +void DolphinViewActionHandler::slotZoomLevelChanged(int current, int previous) { + Q_UNUSED(previous); + QAction* zoomInAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomIn)); if (zoomInAction) { - zoomInAction->setEnabled(level < ZoomLevelInfo::maximumLevel()); + zoomInAction->setEnabled(current < ZoomLevelInfo::maximumLevel()); } QAction* zoomOutAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomOut)); if (zoomOutAction) { - zoomOutAction->setEnabled(level > ZoomLevelInfo::minimumLevel()); + zoomOutAction->setEnabled(current > ZoomLevelInfo::minimumLevel()); } } diff --git a/src/views/dolphinviewactionhandler.h b/src/views/dolphinviewactionhandler.h index 72e768375..4e5a0d32f 100644 --- a/src/views/dolphinviewactionhandler.h +++ b/src/views/dolphinviewactionhandler.h @@ -124,7 +124,7 @@ private Q_SLOTS: void togglePreview(bool); /** Updates the state of the 'Show preview' menu action. */ - void slotShowPreviewChanged(); + void slotPreviewsShownChanged(bool shown); /** Increases the size of the current set view mode. */ void zoomIn(); @@ -156,7 +156,7 @@ private Q_SLOTS: /** * Updates the state of the 'Zoom In' and 'Zoom Out' actions. */ - void slotZoomLevelChanged(int level); + void slotZoomLevelChanged(int current, int previous); /** * Switches on or off the displaying of additional information @@ -172,7 +172,8 @@ private Q_SLOTS: /** * Updates the state of the 'Additional Information' actions. */ - void slotAdditionalInfoChanged(); + void slotAdditionalInfoListChanged(const QList<DolphinView::AdditionalInfo>& current, + const QList<DolphinView::AdditionalInfo>& previous); /** * Switches between sorting by categories or not. @@ -182,7 +183,7 @@ private Q_SLOTS: /** * Updates the state of the 'Categorized sorting' menu action. */ - void slotCategorizedSortingChanged(); + void slotCategorizedSortingChanged(bool sortCategorized); /** * Switches between showing and hiding of hidden marked files @@ -192,7 +193,7 @@ private Q_SLOTS: /** * Updates the state of the 'Show hidden files' menu action. */ - void slotShowHiddenFilesChanged(); + void slotHiddenFilesShownChanged(bool shown); /** * Opens the view properties dialog, which allows to modify the properties @@ -234,16 +235,16 @@ private: KToggleAction* iconsModeAction(); /** - * Returns the "switch to details mode" action. + * Returns the "switch to compact mode" action. * Helper method for createActions(); */ - KToggleAction* detailsModeAction(); + KToggleAction* compactModeAction(); /** - * Returns the "switch to columns mode" action. + * Returns the "switch to details mode" action. * Helper method for createActions(); */ - KToggleAction* columnsModeAction(); + KToggleAction* detailsModeAction(); KActionCollection* m_actionCollection; DolphinView* m_currentView; diff --git a/src/views/dolphinviewcontroller.cpp b/src/views/dolphinviewcontroller.cpp deleted file mode 100644 index e182e48c1..000000000 --- a/src/views/dolphinviewcontroller.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2010 by Peter Penz <[email protected]> * - * * - * 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 "dolphinviewcontroller.h" -#include "zoomlevelinfo.h" - -#include <KDirModel> -#include <KFileItemActions> -#include <QAbstractProxyModel> -#include <QApplication> -#include <QClipboard> -#include <QDir> - -Qt::MouseButtons DolphinViewController::m_mouseButtons = Qt::NoButton; - -DolphinViewController::DolphinViewController(DolphinView* dolphinView) : - QObject(dolphinView), - m_dolphinView(dolphinView), - m_itemView(0), - m_versionControlActions() -{ -} - -DolphinViewController::~DolphinViewController() -{ -} - -const DolphinView* DolphinViewController::view() const -{ - return m_dolphinView; -} - -void DolphinViewController::requestUrlChange(const KUrl& url) -{ - emit urlChangeRequested(url); -} - -void DolphinViewController::setItemView(QAbstractItemView* view) -{ - if (m_itemView) { - disconnect(m_itemView, SIGNAL(pressed(const QModelIndex&)), - this, SLOT(updateMouseButtonState())); - } - - m_itemView = view; - - if (m_itemView) { - // TODO: this is a workaround until Qt-issue 176832 has been fixed - connect(m_itemView, SIGNAL(pressed(const QModelIndex&)), - this, SLOT(updateMouseButtonState())); - } -} - -QAbstractItemView* DolphinViewController::itemView() const -{ - return m_itemView; -} - -void DolphinViewController::triggerContextMenuRequest(const QPoint& pos, - const QList<QAction*>& customActions) -{ - emit activated(); - emit requestContextMenu(pos, customActions); -} - -void DolphinViewController::requestActivation() -{ - emit activated(); -} - -void DolphinViewController::indicateDroppedUrls(const KFileItem& destItem, QDropEvent* event) -{ - emit urlsDropped(destItem, m_dolphinView->url(), event); -} - - -void DolphinViewController::indicateSortingChange(DolphinView::Sorting sorting) -{ - emit sortingChanged(sorting); -} - -void DolphinViewController::indicateSortOrderChange(Qt::SortOrder order) -{ - emit sortOrderChanged(order); -} - -void DolphinViewController::indicateSortFoldersFirstChange(bool foldersFirst) -{ - emit sortFoldersFirstChanged(foldersFirst); -} - -void DolphinViewController::indicateAdditionalInfoChange(const KFileItemDelegate::InformationList& info) -{ - emit additionalInfoChanged(info); -} - -void DolphinViewController::setVersionControlActions(QList<QAction*> actions) -{ - m_versionControlActions = actions; -} - -QList<QAction*> DolphinViewController::versionControlActions(const KFileItemList& items) -{ - emit requestVersionControlActions(items); - // All view implementations are connected with the signal requestVersionControlActions() - // (see ViewExtensionFactory) and will invoke DolphinViewController::setVersionControlActions(), - // so that the context dependent actions can be returned. - return m_versionControlActions; -} - -void DolphinViewController::handleKeyPressEvent(QKeyEvent* event) -{ - if (!m_itemView) { - return; - } - - const QItemSelectionModel* selModel = m_itemView->selectionModel(); - const QModelIndex currentIndex = selModel->currentIndex(); - const bool trigger = currentIndex.isValid() - && ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Enter)) - && !selModel->selectedIndexes().isEmpty(); - if (!trigger) { - return; - } - - // Collect selected files and selected directories - // as two separate lists. - QModelIndexList dirQueue; - const QModelIndexList indexList = selModel->selectedIndexes(); - KFileItemList fileOpenList; - foreach (const QModelIndex& index, indexList) { - if (itemForIndex(index).isDir()) { - dirQueue << index; - } else { - fileOpenList << itemForIndex(index); - } - } - - // Handle selected files - if (fileOpenList.count() == 1) { - emit itemTriggered(fileOpenList.first()); - } else { - KFileItemActions fileItemActions; - fileItemActions.runPreferredApplications(fileOpenList, "DesktopEntryName != 'dolphin'"); - } - - // Handle selected directories - if (dirQueue.count() == 1) { - // Open directory in the view - emit itemTriggered(itemForIndex(dirQueue[0])); - } else { - // Open directories in separate tabs - foreach(const QModelIndex& dir, dirQueue) { - emit tabRequested(itemForIndex(dir).url()); - } - } -} - -void DolphinViewController::replaceUrlByClipboard() -{ - const QClipboard* clipboard = QApplication::clipboard(); - QString text; - if (clipboard->mimeData(QClipboard::Selection)->hasText()) { - text = clipboard->mimeData(QClipboard::Selection)->text(); - } else if (clipboard->mimeData(QClipboard::Clipboard)->hasText()) { - text = clipboard->mimeData(QClipboard::Clipboard)->text(); - } - if (!text.isEmpty() && QDir::isAbsolutePath(text)) { - m_dolphinView->setUrl(KUrl(text)); - } -} - -void DolphinViewController::requestToolTipHiding() -{ - emit hideToolTip(); -} - -void DolphinViewController::emitItemTriggered(const KFileItem& item) -{ - emit itemTriggered(item); -} - -KFileItem DolphinViewController::itemForIndex(const QModelIndex& index) const -{ - if (m_itemView) { - QAbstractProxyModel* proxyModel = static_cast<QAbstractProxyModel*>(m_itemView->model()); - if (proxyModel) { - KDirModel* dirModel = static_cast<KDirModel*>(proxyModel->sourceModel()); - const QModelIndex dirIndex = proxyModel->mapToSource(index); - return dirModel->itemForIndex(dirIndex); - } - } - - return KFileItem(); -} - -void DolphinViewController::triggerItem(const QModelIndex& index) -{ - if (m_mouseButtons & Qt::LeftButton) { - const KFileItem item = itemForIndex(index); - if (index.isValid() && (index.column() == KDirModel::Name)) { - emit itemTriggered(item); - } else if (m_itemView) { - m_itemView->clearSelection(); - emit itemEntered(KFileItem()); - } - } -} - -void DolphinViewController::requestTab(const QModelIndex& index) -{ - if (m_mouseButtons & Qt::MidButton) { - const KFileItem item = itemForIndex(index); - const bool validRequest = index.isValid() && - (index.column() == KDirModel::Name) && - (item.isDir() || m_dolphinView->isTabsForFilesEnabled()); - if (validRequest) { - emit tabRequested(item.url()); - } - } -} - -void DolphinViewController::emitItemEntered(const QModelIndex& index) -{ - KFileItem item = itemForIndex(index); - if (!item.isNull()) { - emit itemEntered(item); - } -} - -void DolphinViewController::emitViewportEntered() -{ - emit viewportEntered(); -} - -void DolphinViewController::updateMouseButtonState() -{ - m_mouseButtons = QApplication::mouseButtons(); -} - -#include "dolphinviewcontroller.moc" diff --git a/src/views/dolphinviewcontroller.h b/src/views/dolphinviewcontroller.h deleted file mode 100644 index 0616e7fcd..000000000 --- a/src/views/dolphinviewcontroller.h +++ /dev/null @@ -1,311 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2010 by Peter Penz <[email protected]> * - * * - * 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 DOLPHINVIEWCONTROLLER_H -#define DOLPHINVIEWCONTROLLER_H - -#include <views/dolphinview.h> -#include <KUrl> -#include <QObject> -#include <libdolphin_export.h> - -class QAbstractItemView; -class DolphinView; -class KUrl; -class QPoint; - -/** - * @brief Allows the view mode implementations (DolphinIconsView, DolphinDetailsView, DolphinColumnView) - * to do a limited control of the DolphinView. - * - * The DolphinView connects to the signals of DolphinViewController to react on changes - * that have been triggered by the view mode implementations. The view mode implementations - * have read access to the whole DolphinView by DolphinViewController::view(). Limited control of the - * DolphinView by the view mode implementations are defined by the public DolphinViewController methods. - */ -class LIBDOLPHINPRIVATE_EXPORT DolphinViewController : public QObject -{ - Q_OBJECT - -public: - explicit DolphinViewController(DolphinView* dolphinView); - virtual ~DolphinViewController(); - - /** - * Allows read access for the view mode implementation - * to the DolphinView. - */ - const DolphinView* view() const; - - /** - * Requests the DolphinView to change the URL to \p url. The signal - * urlChangeRequested will be emitted. - */ - void requestUrlChange(const KUrl& url); - - /** - * Changes the current view mode implementation where the controller is working. - * This is only necessary for views like the column view, where internally - * several QAbstractItemView instances are used. - */ - void setItemView(QAbstractItemView* view); - QAbstractItemView* itemView() const; - - /** - * Requests a context menu for the position \a pos. DolphinView - * takes care itself to get the selected items depending from - * \a pos. It is possible to define a custom list of actions for - * the context menu by \a customActions. - */ - void triggerContextMenuRequest(const QPoint& pos, - const QList<QAction*>& customActions = QList<QAction*>()); - - /** - * Requests an activation of the DolphinView and emits the signal - * activated(). This method should be invoked by the view mode implementation - * if e. g. a mouse click on the view has been done. - */ - void requestActivation(); - - /** - * Indicates that URLs are dropped above a destination. The DolphinView - * will start the corresponding action (copy, move, link). - * @param destItem Item of the destination (can be null, see KFileItem::isNull()). - * @param event Drop event - */ - void indicateDroppedUrls(const KFileItem& destItem, QDropEvent* event); - - /** - * Informs the DolphinView about a sorting change done inside - * the view mode implementation. - */ - void indicateSortingChange(DolphinView::Sorting sorting); - - /** - * Informs the DolphinView about a sort order change done inside - * the view mode implementation. - */ - void indicateSortOrderChange(Qt::SortOrder order); - - /** - * Informs the DolphinView about a change between separate sorting - * (with folders first) and mixed sorting of files and folders done inside - * the view mode implementation. - */ - void indicateSortFoldersFirstChange(bool foldersFirst); - - /** - * Informs the DolphinView about an additional information change - * done inside the view mode implementation. - */ - void indicateAdditionalInfoChange(const KFileItemDelegate::InformationList& info); - - /** - * Sets the available version control actions. Is called by the view - * mode implementation as soon as the DolphinView has requested them by the signal - * requestVersionControlActions(). - */ - void setVersionControlActions(QList<QAction*> actions); - - /** - * Emits the signal requestVersionControlActions(). The view mode implementation - * listens to the signal and will invoke a DolphinViewController::setVersionControlActions() - * and the result will be returned. - */ - QList<QAction*> versionControlActions(const KFileItemList& items); - - /** - * Must be be invoked in each view mode implementation whenever a key has been - * pressed. If the selection model of \a view is not empty and - * the return key has been pressed, the selected items will get triggered. - */ - void handleKeyPressEvent(QKeyEvent* event); - - /** - * Replaces the URL of the DolphinView with the content - * of the clipboard as URL. If the clipboard contains no text, - * nothing will be done. - */ - void replaceUrlByClipboard(); - - /** - * Requests the view mode implementation to hide tooltips. - */ - void requestToolTipHiding(); - - /** - * Emits the signal itemTriggered() for the item \a item. - * The method can be used by the view mode implementations to - * trigger an item directly without mouse interaction. - * If the item triggering is done by the mouse, it is recommended - * to use DolphinViewController::triggerItem(), as this will check - * the used mouse buttons to execute the correct action. - */ - void emitItemTriggered(const KFileItem& item); - - /** - * Returns the file item for the proxy index \a index of the DolphinView. - */ - KFileItem itemForIndex(const QModelIndex& index) const; - -public slots: - /** - * Emits the signal itemTriggered() if the file item for the index \a index - * is not null and the left mouse button has been pressed. If the item is - * null, the signal itemEntered() is emitted. - * The method should be invoked by the view mode implementations whenever the - * user has triggered an item with the mouse (see - * QAbstractItemView::clicked() or QAbstractItemView::doubleClicked()). - */ - void triggerItem(const QModelIndex& index); - - /** - * Emits the signal tabRequested(), if the file item for the index \a index - * represents a directory and when the middle mouse button has been pressed. - */ - void requestTab(const QModelIndex& index); - - /** - * Emits the signal itemEntered() if the file item for the index \a index - * is not null. The method should be invoked by the view mode implementation - * whenever the mouse cursor is above an item. - */ - void emitItemEntered(const QModelIndex& index); - - /** - * Emits the signal viewportEntered(). The method should be invoked by - * the view mode implementation whenever the mouse cursor is above the viewport. - */ - void emitViewportEntered(); - -signals: - void urlChangeRequested(const KUrl& url); - - /** - * Is emitted if a context menu should be opened (see triggerContextMenuRequest()). - * @param pos Position relative to the view widget where the - * context menu should be opened. It is recommended - * to get the corresponding model index from - * this position. - * @param customActions List of actions that is added to the context menu when - * the menu is opened above the viewport. - */ - void requestContextMenu(const QPoint& pos, QList<QAction*> customActions); - - /** - * Is emitted if the view has been activated by e. g. a mouse click. - */ - void activated(); - - /** - * Is emitted if URLs have been dropped to the destination - * path \a destPath. If the URLs have been dropped above an item of - * the destination path, the item is indicated by \a destItem - * (can be null, see KFileItem::isNull()). - */ - void urlsDropped(const KFileItem& destItem, - const KUrl& destPath, - QDropEvent* event); - - /** - * Is emitted if the sorting has been changed to \a sorting by - * the view mode implementation (see indicateSortingChanged(). - * The DolphinView connects to - * this signal to update its menu action. - */ - void sortingChanged(DolphinView::Sorting sorting); - - /** - * Is emitted if the sort order has been changed to \a order - * by the view mode implementation (see indicateSortOrderChanged(). - * The DolphinView connects - * to this signal to update its menu actions. - */ - void sortOrderChanged(Qt::SortOrder order); - - /** - * Is emitted if 'sort folders first' has been changed to \a foldersFirst - * by the view mode implementation (see indicateSortOrderChanged(). - * The DolphinView connects - * to this signal to update its menu actions. - */ - void sortFoldersFirstChanged(bool foldersFirst); - - /** - * Is emitted if the additional info has been changed to \a info - * by the view mode implementation. The DolphinView connects - * to this signal to update its menu actions. - */ - void additionalInfoChanged(const KFileItemDelegate::InformationList& info); - - /** - * Is emitted if the item \a item should be triggered. The abstract - * Dolphin view connects to this signal. If the item represents a directory, - * the directory is opened. On a file the corresponding application is opened. - * The item is null (see KFileItem::isNull()), when clicking on the viewport itself. - */ - void itemTriggered(const KFileItem& item); - - /** - * Is emitted if the mouse cursor has entered the item - * given by \a index (see emitItemEntered()). - */ - void itemEntered(const KFileItem& item); - - /** - * Is emitted if a new tab should be opened for the URL \a url. - */ - void tabRequested(const KUrl& url); - - /** - * Is emitted if the mouse cursor has entered - * the viewport (see emitViewportEntered()). - */ - void viewportEntered(); - - /** - * Is emitted, if DolphinViewController::requestToolTipHiding() is invoked - * and requests to hide all tooltips. - */ - void hideToolTip(); - - /** - * Is emitted if pending previews should be canceled (e. g. because of an URL change). - */ - void cancelPreviews(); - - /** - * Requests the view mode implementation to invoke DolphinViewController::setVersionControlActions(), - * so that they can be returned with DolphinViewController::versionControlActions() for - * the DolphinView. - */ - void requestVersionControlActions(const KFileItemList& items); - -private slots: - void updateMouseButtonState(); - -private: - static Qt::MouseButtons m_mouseButtons; // TODO: this is a workaround until Qt-issue 176832 has been fixed - - DolphinView* m_dolphinView; - QAbstractItemView* m_itemView; - QList<QAction*> m_versionControlActions; -}; - -#endif diff --git a/src/views/renamedialog.cpp b/src/views/renamedialog.cpp index 830884dc2..c0c6ad58c 100644 --- a/src/views/renamedialog.cpp +++ b/src/views/renamedialog.cpp @@ -65,12 +65,9 @@ RenameDialog::RenameDialog(QWidget *parent, const KFileItemList& items) : QLabel* editLabel = 0; if (m_renameOneItem) { - m_newName = items.first().name(); - editLabel = new QLabel(i18nc("@label:textbox", "Rename the item <filename>%1</filename> to:", - KStringHandler::csqueeze(m_newName)), page); - if (m_newName.size() > 40) { - editLabel->setToolTip(m_newName); // Set the filename as a the tool tip... - } + m_newName = items.first().name(); + editLabel = new QLabel(i18nc("@label:textbox", "Rename the item <filename>%1</filename> to:", m_newName), + page); } else { m_newName = i18nc("@info:status", "New name #"); editLabel = new QLabel(i18ncp("@label:textbox", diff --git a/src/views/selectionmanager.cpp b/src/views/selectionmanager.cpp deleted file mode 100644 index 7a9e81412..000000000 --- a/src/views/selectionmanager.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Peter Penz <[email protected]> * - * * - * 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 "selectionmanager.h" - -#include "dolphinmodel.h" -#include "dolphin_generalsettings.h" -#include "selectiontoggle.h" -#include "settings/dolphinsettings.h" -#include <KDirModel> -#include <KGlobalSettings> -#include <KIconEffect> - -#include <QAbstractButton> -#include <QAbstractItemView> -#include <QAbstractProxyModel> -#include <QApplication> -#include <QModelIndex> -#include <QPainter> -#include <QPaintEvent> -#include <QRect> -#include <QTimeLine> - -SelectionManager::SelectionManager(QAbstractItemView* parent) : - QObject(parent), - m_view(parent), - m_toggle(0), - m_connected(false), - m_appliedPointingHandCursor(false) -{ - connect(parent, SIGNAL(entered(const QModelIndex&)), - this, SLOT(slotEntered(const QModelIndex&))); - connect(parent, SIGNAL(viewportEntered()), - this, SLOT(slotViewportEntered())); - - const GeneralSettings* settings = DolphinSettings::instance().generalSettings(); - if (settings->showSelectionToggle()) { - m_toggle = new SelectionToggle(m_view->viewport()); - m_toggle->setCheckable(true); - m_toggle->hide(); - connect(m_toggle, SIGNAL(clicked(bool)), - this, SLOT(setItemSelected(bool))); - m_toggle->installEventFilter(this); - } - - m_view->viewport()->installEventFilter(this); -} - -SelectionManager::~SelectionManager() -{ -} - -bool SelectionManager::eventFilter(QObject* watched, QEvent* event) -{ - if (watched == m_view->viewport()) { - switch (event->type()) { - case QEvent::Leave: - if (m_toggle) { - m_toggle->hide(); - } - restoreCursor(); - break; - - case QEvent::MouseButtonPress: { - // Set the toggle invisible, if a mouse button has been pressed - // outside the toggle boundaries. This e.g. assures, that the toggle - // gets invisible during dragging items. - if (m_toggle) { - const QRect toggleBounds(m_toggle->mapToGlobal(QPoint(0, 0)), m_toggle->size()); - m_toggle->setVisible(toggleBounds.contains(QCursor::pos())); - } - break; - } - - default: - break; - } - } else if (watched == m_toggle) { - switch (event->type()) { - case QEvent::Enter: - QApplication::changeOverrideCursor(Qt::ArrowCursor); - break; - - case QEvent::Leave: - QApplication::changeOverrideCursor(Qt::PointingHandCursor); - break; - - default: - break; - } - } - - return QObject::eventFilter(watched, event); -} - -void SelectionManager::reset() -{ - if (m_toggle) { - m_toggle->reset(); - } -} - -void SelectionManager::slotEntered(const QModelIndex& index) -{ - const bool isSelectionCandidate = index.isValid() && - (index.column() == DolphinModel::Name) && - (QApplication::mouseButtons() == Qt::NoButton); - - restoreCursor(); - if (isSelectionCandidate && KGlobalSettings::singleClick()) { - applyPointingHandCursor(); - } - - if (isSelectionCandidate) { - if (!m_connected) { - connect(m_view->model(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)), - this, SLOT(slotRowsRemoved(const QModelIndex&, int, int))); - connect(m_view->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, - SLOT(slotSelectionChanged(const QItemSelection&, const QItemSelection&))); - m_connected = true; - } - } else { - disconnect(m_view->model(), SIGNAL(rowsRemoved(const QModelIndex&, int, int)), - this, SLOT(slotRowsRemoved(const QModelIndex&, int, int))); - disconnect(m_view->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, - SLOT(slotSelectionChanged(const QItemSelection&, const QItemSelection&))); - m_connected = false; - } - - if (!m_toggle) { - return; - } - - m_toggle->hide(); - if (isSelectionCandidate) { - m_toggle->setUrl(urlForIndex(index)); - - // Increase the size of the toggle for large items - const int iconHeight = m_view->iconSize().height(); - - int toggleSize = KIconLoader::SizeSmall; - if (iconHeight >= KIconLoader::SizeEnormous) { - toggleSize = KIconLoader::SizeMedium; - } else if (iconHeight >= KIconLoader::SizeLarge) { - toggleSize = KIconLoader::SizeSmallMedium; - } - - // Add a small invisible margin, if the item-height is nearly - // equal to the toggleSize (#169494). - const QRect rect = m_view->visualRect(index); - int margin = (rect.height() - toggleSize) / 2; - if (margin > 4) { - margin = 0; - } - toggleSize += 2 * margin; - m_toggle->setMargin(margin); - m_toggle->resize(toggleSize, toggleSize); - m_toggle->move(rect.topLeft()); - - QItemSelectionModel* selModel = m_view->selectionModel(); - m_toggle->setChecked(selModel->isSelected(index)); - m_toggle->show(); - } else { - m_toggle->setUrl(KUrl()); - } -} - -void SelectionManager::slotViewportEntered() -{ - if (m_toggle) { - m_toggle->hide(); - } - restoreCursor(); -} - -void SelectionManager::setItemSelected(bool selected) -{ - emit selectionChanged(); - - if (m_toggle && !m_toggle->url().isEmpty()) { - const QModelIndex index = indexForUrl(m_toggle->url()); - if (index.isValid()) { - QItemSelectionModel* selModel = m_view->selectionModel(); - if (selected) { - selModel->select(index, QItemSelectionModel::Select); - } else { - selModel->select(index, QItemSelectionModel::Deselect); - } - selModel->setCurrentIndex(index, QItemSelectionModel::Current); - } - } -} - -void SelectionManager::slotRowsRemoved(const QModelIndex& parent, int start, int end) -{ - Q_UNUSED(parent); - Q_UNUSED(start); - Q_UNUSED(end); - if (m_toggle) { - m_toggle->hide(); - } - restoreCursor(); -} - -void SelectionManager::slotSelectionChanged(const QItemSelection& selected, - const QItemSelection& deselected) -{ - // The selection has been changed outside the scope of the selection manager - // (e. g. by the rubberband or the "Select All" action). Take care updating - // the state of the toggle button. - if (m_toggle && !m_toggle->url().isEmpty()) { - const QModelIndex index = indexForUrl(m_toggle->url()); - if (index.isValid()) { - if (selected.contains(index)) { - m_toggle->setChecked(true); - } - - if (deselected.contains(index)) { - m_toggle->setChecked(false); - } - } - } -} - -KUrl SelectionManager::urlForIndex(const QModelIndex& index) const -{ - QAbstractProxyModel* proxyModel = static_cast<QAbstractProxyModel*>(m_view->model()); - KDirModel* dirModel = static_cast<KDirModel*>(proxyModel->sourceModel()); - const QModelIndex dirIndex = proxyModel->mapToSource(index); - return dirModel->itemForIndex(dirIndex).url(); -} - -const QModelIndex SelectionManager::indexForUrl(const KUrl& url) const -{ - QAbstractProxyModel* proxyModel = static_cast<QAbstractProxyModel*>(m_view->model()); - KDirModel* dirModel = static_cast<KDirModel*>(proxyModel->sourceModel()); - const QModelIndex dirIndex = dirModel->indexForUrl(url); - return proxyModel->mapFromSource(dirIndex); -} - - -void SelectionManager::applyPointingHandCursor() -{ - if (!m_appliedPointingHandCursor) { - QApplication::setOverrideCursor(QCursor(Qt::PointingHandCursor)); - m_appliedPointingHandCursor = true; - } -} - -void SelectionManager::restoreCursor() -{ - if (m_appliedPointingHandCursor) { - QApplication::restoreOverrideCursor(); - m_appliedPointingHandCursor = false; - } -} - -#include "selectionmanager.moc" diff --git a/src/views/selectionmanager.h b/src/views/selectionmanager.h deleted file mode 100644 index 7a3a1946a..000000000 --- a/src/views/selectionmanager.h +++ /dev/null @@ -1,78 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Peter Penz <[email protected]> * - * * - * 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 SELECTIONMANAGER_H -#define SELECTIONMANAGER_H - -#include <KFileItem> - -#include <QObject> - -class QAbstractItemView; -class QModelIndex; -class QItemSelection; -class SelectionToggle; - -/** - * @brief Allows to select and deselect items for item views. - * - * Whenever an item is hovered by the mouse, a toggle button is shown - * which allows to select/deselect the current item. - */ -class SelectionManager : public QObject -{ - Q_OBJECT - -public: - SelectionManager(QAbstractItemView* parent); - virtual ~SelectionManager(); - virtual bool eventFilter(QObject* watched, QEvent* event); - -public slots: - /** - * Resets the selection manager so that the toggle button gets - * invisible. - */ - void reset(); - -signals: - /** Is emitted if the selection has been changed by the toggle button. */ - void selectionChanged(); - -private slots: - void slotEntered(const QModelIndex& index); - void slotViewportEntered(); - void setItemSelected(bool selected); - void slotRowsRemoved(const QModelIndex& parent, int start, int end); - void slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); - -private: - KUrl urlForIndex(const QModelIndex& index) const; - const QModelIndex indexForUrl(const KUrl& url) const; - void applyPointingHandCursor(); - void restoreCursor(); - -private: - QAbstractItemView* m_view; - SelectionToggle* m_toggle; - bool m_connected; - bool m_appliedPointingHandCursor; -}; - -#endif diff --git a/src/views/selectiontoggle.cpp b/src/views/selectiontoggle.cpp deleted file mode 100644 index d602600c5..000000000 --- a/src/views/selectiontoggle.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Peter Penz <[email protected]> * - * * - * 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 "selectiontoggle.h" - -#include <KGlobalSettings> -#include <KIcon> -#include <KIconLoader> -#include <KIconEffect> -#include <KLocale> - -#include <QApplication> -#include <QPainter> -#include <QPaintEvent> -#include <QRect> -#include <QTimer> -#include <QTimeLine> - -#include <KDebug> - -SelectionToggle::SelectionToggle(QWidget* parent) : - QAbstractButton(parent), - m_isHovered(false), - m_leftMouseButtonPressed(false), - m_fadingValue(0), - m_margin(0), - m_icon(), - m_fadingTimeLine(0) -{ - setFocusPolicy(Qt::NoFocus); - parent->installEventFilter(this); - resize(sizeHint()); - setIconOverlay(isChecked()); - connect(this, SIGNAL(toggled(bool)), - this, SLOT(setIconOverlay(bool))); - connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)), - this, SLOT(refreshIcon())); -} - -SelectionToggle::~SelectionToggle() -{ -} - -QSize SelectionToggle::sizeHint() const -{ - return QSize(16, 16); -} - -void SelectionToggle::reset() -{ - m_url = KUrl(); - hide(); -} - -void SelectionToggle::setUrl(const KUrl& url) -{ - m_url = url; - if (!url.isEmpty()) { - startFading(); - } -} - -void SelectionToggle::setMargin(int margin) -{ - if (margin != m_margin) { - m_margin = margin; - update(); - } -} - -int SelectionToggle::margin() const -{ - return m_margin; -} - -KUrl SelectionToggle::url() const -{ - return m_url; -} - -void SelectionToggle::setVisible(bool visible) -{ - QAbstractButton::setVisible(visible); - - stopFading(); - if (visible) { - startFading(); - } - -} - -bool SelectionToggle::eventFilter(QObject* obj, QEvent* event) -{ - if ((obj == parent()) && (event->type() == QEvent::MouseMove) && m_leftMouseButtonPressed) { - // Don't forward mouse move events to the viewport, - // otherwise a rubberband selection will be shown when - // clicking on the selection toggle and moving the mouse - // above the viewport. - return true; - } - - return QAbstractButton::eventFilter(obj, event); -} - -void SelectionToggle::enterEvent(QEvent* event) -{ - QAbstractButton::enterEvent(event); - - // if the mouse cursor is above the selection toggle, display - // it immediately without fading timer - m_isHovered = true; - if (m_fadingTimeLine) { - m_fadingTimeLine->stop(); - } - m_fadingValue = 255; - setToolTip(isChecked() ? i18nc("@info:tooltip", "Deselect Item") : - i18nc("@info:tooltip", "Select Item")); - update(); -} - -void SelectionToggle::leaveEvent(QEvent* event) -{ - QAbstractButton::leaveEvent(event); - - m_isHovered = false; - update(); -} - -void SelectionToggle::mousePressEvent(QMouseEvent* event) -{ - QAbstractButton::mousePressEvent(event); - m_leftMouseButtonPressed = (event->buttons() & Qt::LeftButton); -} - -void SelectionToggle::mouseReleaseEvent(QMouseEvent* event) -{ - QAbstractButton::mouseReleaseEvent(event); - m_leftMouseButtonPressed = (event->buttons() & Qt::LeftButton); -} - -void SelectionToggle::resizeEvent(QResizeEvent* event) -{ - QAbstractButton::resizeEvent(event); - setIconOverlay(isChecked()); -} - -void SelectionToggle::paintEvent(QPaintEvent* event) -{ - QPainter painter(this); - painter.setClipRect(event->rect()); - - // draw the icon overlay - const QPoint pos(m_margin, m_margin); - if (m_isHovered) { - KIconEffect *iconEffect = KIconLoader::global()->iconEffect(); - QPixmap activeIcon = iconEffect->apply(m_icon, KIconLoader::Desktop, KIconLoader::ActiveState); - painter.drawPixmap(pos, activeIcon); - } else { - if (m_fadingValue < 255) { - // apply an alpha mask respecting the fading value to the icon - QPixmap icon = m_icon; - QPixmap alphaMask(icon.width(), icon.height()); - const QColor color(m_fadingValue, m_fadingValue, m_fadingValue); - alphaMask.fill(color); - icon.setAlphaChannel(alphaMask); - painter.drawPixmap(pos, icon); - } else { - // no fading is required - painter.drawPixmap(pos, m_icon); - } - } - -} - -void SelectionToggle::setFadingValue(int value) -{ - m_fadingValue = value; - if (m_fadingValue >= 255) { - Q_ASSERT(m_fadingTimeLine); - m_fadingTimeLine->stop(); - } - update(); -} - -void SelectionToggle::setIconOverlay(bool checked) -{ - const char* icon = checked ? "list-remove" : "list-add"; - const int size = qMin(width() - 2 * m_margin, height() - 2 * m_margin); - m_icon = KIconLoader::global()->loadIcon(icon, - KIconLoader::NoGroup, - size); - update(); -} - -void SelectionToggle::refreshIcon() -{ - setIconOverlay(isChecked()); -} - -void SelectionToggle::startFading() -{ - Q_ASSERT(!m_fadingTimeLine); - - const bool animate = KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects; - const int duration = animate ? 600 : 1; - - m_fadingTimeLine = new QTimeLine(duration, this); - connect(m_fadingTimeLine, SIGNAL(frameChanged(int)), - this, SLOT(setFadingValue(int))); - m_fadingTimeLine->setFrameRange(0, 255); - m_fadingTimeLine->start(); - m_fadingValue = 0; -} - -void SelectionToggle::stopFading() -{ - if (m_fadingTimeLine) { - m_fadingTimeLine->stop(); - delete m_fadingTimeLine; - m_fadingTimeLine = 0; - } - m_fadingValue = 0; -} - -#include "selectiontoggle.moc" diff --git a/src/views/selectiontoggle.h b/src/views/selectiontoggle.h deleted file mode 100644 index 221ab0745..000000000 --- a/src/views/selectiontoggle.h +++ /dev/null @@ -1,99 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008 by Peter Penz <[email protected]> * - * * - * 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 SELECTIONTOGGLE_H -#define SELECTIONTOGGLE_H - -#include <KUrl> - -#include <QAbstractButton> -#include <QPixmap> - -class QTimeLine; - -/** - * @brief Toggle button for changing the selection of an hovered item. - * - * The toggle button is visually invisible until it is displayed at least - * for one second. - * - * @see SelectionManager - */ -class SelectionToggle : public QAbstractButton -{ - Q_OBJECT - -public: - explicit SelectionToggle(QWidget* parent); - virtual ~SelectionToggle(); - virtual QSize sizeHint() const; - - /** - * Resets the selection toggle so that it is hidden and stays - * visually invisible for at least one second after it is shown again. - */ - void reset(); - - void setUrl(const KUrl& url); - KUrl url() const; - - /** - * Sets the margin around the selection-icon in pixels. Per default - * the value is 0. - */ - void setMargin(int margin); - int margin() const; - -public slots: - virtual void setVisible(bool visible); - -protected: - virtual bool eventFilter(QObject* obj, QEvent* event); - virtual void enterEvent(QEvent* event); - virtual void leaveEvent(QEvent* event); - virtual void mousePressEvent(QMouseEvent* event); - virtual void mouseReleaseEvent(QMouseEvent* event); - virtual void resizeEvent(QResizeEvent* event); - virtual void paintEvent(QPaintEvent* event); - -private slots: - /** - * Sets the alpha value for the fading animation and is - * connected with m_fadingTimeLine. - */ - void setFadingValue(int value); - - void setIconOverlay(bool checked); - void refreshIcon(); - -private: - void startFading(); - void stopFading(); - -private: - bool m_isHovered; - bool m_leftMouseButtonPressed; - int m_fadingValue; - int m_margin; - QPixmap m_icon; - QTimeLine* m_fadingTimeLine; - KUrl m_url; -}; - -#endif diff --git a/src/views/tooltips/tooltipmanager.cpp b/src/views/tooltips/tooltipmanager.cpp index 69ed1e3be..e532bee82 100644 --- a/src/views/tooltips/tooltipmanager.cpp +++ b/src/views/tooltips/tooltipmanager.cpp @@ -26,19 +26,14 @@ #include <QApplication> #include <QDesktopWidget> +#include <QLayout> #include <QScrollArea> #include <QScrollBar> #include <QTimer> -#include <views/dolphinmodel.h> -#include <views/dolphinsortfilterproxymodel.h> - -ToolTipManager::ToolTipManager(QAbstractItemView* parent, - DolphinSortFilterProxyModel* model) : +ToolTipManager::ToolTipManager(QWidget* parent) : QObject(parent), m_view(parent), - m_dolphinModel(0), - m_proxyModel(model), m_showToolTipTimer(0), m_contentRetrievalTimer(0), m_fileMetaDataToolTip(0), @@ -48,11 +43,11 @@ ToolTipManager::ToolTipManager(QAbstractItemView* parent, m_item(), m_itemRect() { - m_dolphinModel = static_cast<DolphinModel*>(m_proxyModel->sourceModel()); - connect(parent, SIGNAL(entered(const QModelIndex&)), - this, SLOT(requestToolTip(const QModelIndex&))); - connect(parent, SIGNAL(viewportEntered()), - this, SLOT(hideToolTip())); + //m_dolphinModel = static_cast<DolphinModel*>(m_proxyModel->sourceModel()); + //connect(parent, SIGNAL(entered(const QModelIndex&)), + // this, SLOT(requestToolTip(const QModelIndex&))); + //connect(parent, SIGNAL(viewportEntered()), + // this, SLOT(hideToolTip())); // Initialize timers m_showToolTipTimer = new QTimer(this); @@ -70,14 +65,14 @@ ToolTipManager::ToolTipManager(QAbstractItemView* parent, // When the mousewheel is used, the items don't get a hovered indication // (Qt-issue #200665). To assure that the tooltip still gets hidden, // the scrollbars are observed. - connect(parent->horizontalScrollBar(), SIGNAL(valueChanged(int)), + /*connect(parent->horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(hideToolTip())); connect(parent->verticalScrollBar(), SIGNAL(valueChanged(int)), - this, SLOT(hideToolTip())); + this, SLOT(hideToolTip()));*/ Q_ASSERT(m_view); - m_view->viewport()->installEventFilter(this); - m_view->installEventFilter(this); + //m_view->viewport()->installEventFilter(this); + //m_view->installEventFilter(this); } ToolTipManager::~ToolTipManager() @@ -103,7 +98,7 @@ void ToolTipManager::hideToolTip() bool ToolTipManager::eventFilter(QObject* watched, QEvent* event) { - if (watched == m_view->viewport()) { + /*if (watched == m_view->viewport()) { switch (event->type()) { case QEvent::Leave: case QEvent::MouseButtonPress: @@ -114,24 +109,25 @@ bool ToolTipManager::eventFilter(QObject* watched, QEvent* event) } } else if ((watched == m_view) && (event->type() == QEvent::KeyPress)) { hideToolTip(); - } + }*/ return QObject::eventFilter(watched, event); } void ToolTipManager::requestToolTip(const QModelIndex& index) { + Q_UNUSED(index); hideToolTip(); // Only request a tooltip for the name column and when no selection or // drag & drop operation is done (indicated by the left mouse button) - if ((index.column() == DolphinModel::Name) && !(QApplication::mouseButtons() & Qt::LeftButton)) { - m_itemRect = m_view->visualRect(index); - const QPoint pos = m_view->viewport()->mapToGlobal(m_itemRect.topLeft()); + if (!(QApplication::mouseButtons() & Qt::LeftButton)) { + m_itemRect = QRect(); //m_view->visualRect(index); + const QPoint pos; // = m_view->viewport()->mapToGlobal(m_itemRect.topLeft()); m_itemRect.moveTo(pos); - const QModelIndex dirIndex = m_proxyModel->mapToSource(index); - m_item = m_dolphinModel->itemForIndex(dirIndex); + //const QModelIndex dirIndex = m_proxyModel->mapToSource(index); + //m_item = m_dolphinModel->itemForIndex(dirIndex); // Only start the retrieving of the content, when the mouse has been over this // item for 200 milliseconds. This prevents a lot of useless preview jobs and diff --git a/src/views/tooltips/tooltipmanager.h b/src/views/tooltips/tooltipmanager.h index f8bcd3dfd..11ef3d3ac 100644 --- a/src/views/tooltips/tooltipmanager.h +++ b/src/views/tooltips/tooltipmanager.h @@ -44,8 +44,7 @@ class ToolTipManager : public QObject Q_OBJECT public: - explicit ToolTipManager(QAbstractItemView* parent, - DolphinSortFilterProxyModel* model); + explicit ToolTipManager(QWidget* parent); virtual ~ToolTipManager(); public slots: @@ -68,7 +67,7 @@ private slots: void showToolTip(); private: - QAbstractItemView* m_view; + QWidget* m_view; DolphinModel* m_dolphinModel; DolphinSortFilterProxyModel* m_proxyModel; diff --git a/src/views/versioncontrol/versioncontrolobserver.cpp b/src/views/versioncontrol/versioncontrolobserver.cpp index 62f50f30b..02329866f 100644 --- a/src/views/versioncontrol/versioncontrolobserver.cpp +++ b/src/views/versioncontrol/versioncontrolobserver.cpp @@ -35,23 +35,21 @@ #include <QMutexLocker> #include <QTimer> -#include <views/dolphinmodel.h> - -VersionControlObserver::VersionControlObserver(QAbstractItemView* view) : +VersionControlObserver::VersionControlObserver(QWidget* view) : QObject(view), m_pendingItemStatesUpdate(false), m_versionedDirectory(false), m_silentUpdate(false), m_view(view), - m_dirLister(0), - m_dolphinModel(0), + //m_dirLister(0), + //m_dolphinModel(0), m_dirVerificationTimer(0), m_plugin(0), m_updateItemStatesThread(0) { Q_ASSERT(view); - QAbstractProxyModel* proxyModel = qobject_cast<QAbstractProxyModel*>(view->model()); + /*QAbstractProxyModel* proxyModel = qobject_cast<QAbstractProxyModel*>(view->model()); m_dolphinModel = proxyModel ? qobject_cast<DolphinModel*>(proxyModel->sourceModel()) : qobject_cast<DolphinModel*>(view->model()); @@ -71,7 +69,7 @@ VersionControlObserver::VersionControlObserver(QAbstractItemView* view) : m_dirVerificationTimer->setInterval(500); connect(m_dirVerificationTimer, SIGNAL(timeout()), this, SLOT(verifyDirectory())); - } + }*/ } VersionControlObserver::~VersionControlObserver() @@ -133,7 +131,7 @@ void VersionControlObserver::silentDirectoryVerification() void VersionControlObserver::verifyDirectory() { - const KUrl versionControlUrl = m_dirLister->url(); + const KUrl versionControlUrl; // = m_dirLister->url(); if (!versionControlUrl.isLocalFile()) { return; } @@ -143,7 +141,7 @@ void VersionControlObserver::verifyDirectory() } m_plugin = searchPlugin(versionControlUrl); - if (m_plugin) { + /*if (m_plugin) { connect(m_plugin, SIGNAL(versionStatesChanged()), this, SLOT(silentDirectoryVerification())); connect(m_plugin, SIGNAL(infoMessage(QString)), @@ -176,7 +174,7 @@ void VersionControlObserver::verifyDirectory() this, SLOT(delayedDirectoryVerification())); disconnect(m_dirLister, SIGNAL(newItems(const KFileItemList&)), this, SLOT(delayedDirectoryVerification())); - } + }*/ } void VersionControlObserver::slotThreadFinished() @@ -195,7 +193,7 @@ void VersionControlObserver::slotThreadFinished() // (a detailed description of the root cause is given in the class KFilePreviewGenerator // from kdelibs). To bypass this bottleneck, the signals of the model are temporary blocked. // This works as the update of the data does not require a relayout of the views used in Dolphin. - const bool signalsBlocked = m_dolphinModel->signalsBlocked(); + /*const bool signalsBlocked = m_dolphinModel->signalsBlocked(); m_dolphinModel->blockSignals(true); const QList<ItemState> itemStates = m_updateItemStatesThread->itemStates(); @@ -218,7 +216,7 @@ void VersionControlObserver::slotThreadFinished() if (m_pendingItemStatesUpdate) { m_pendingItemStatesUpdate = false; updateItemStates(); - } + }*/ } void VersionControlObserver::updateItemStates() @@ -249,7 +247,9 @@ void VersionControlObserver::updateItemStates() void VersionControlObserver::addDirectory(const QModelIndex& parentIndex, QList<ItemState>& itemStates) { - const int rowCount = m_dolphinModel->rowCount(parentIndex); + Q_UNUSED(parentIndex); + Q_UNUSED(itemStates); + /*const int rowCount = m_dolphinModel->rowCount(parentIndex); for (int row = 0; row < rowCount; ++row) { const QModelIndex index = m_dolphinModel->index(row, DolphinModel::Version, parentIndex); addDirectory(index, itemStates); @@ -260,7 +260,7 @@ void VersionControlObserver::addDirectory(const QModelIndex& parentIndex, QList< itemState.version = KVersionControlPlugin::UnversionedVersion; itemStates.append(itemState); - } + }*/ } KVersionControlPlugin* VersionControlObserver::searchPlugin(const KUrl& directory) const @@ -296,7 +296,8 @@ KVersionControlPlugin* VersionControlObserver::searchPlugin(const KUrl& director // Verify whether the current directory contains revision information // like .svn, .git, ... - foreach (KVersionControlPlugin* plugin, plugins) { + Q_UNUSED(directory); + /*foreach (KVersionControlPlugin* plugin, plugins) { // Use the KDirLister cache to check for .svn, .git, ... files KUrl dirUrl(directory); KUrl fileUrl = dirUrl; @@ -324,14 +325,14 @@ KVersionControlPlugin* VersionControlObserver::searchPlugin(const KUrl& director upUrl = dirUrl.upUrl(); } } - } + }*/ return 0; } bool VersionControlObserver::isVersioned() const { - return m_dolphinModel->hasVersionData() && m_plugin; + return false; //m_dolphinModel->hasVersionData() && m_plugin; } #include "versioncontrolobserver.moc" diff --git a/src/views/versioncontrol/versioncontrolobserver.h b/src/views/versioncontrol/versioncontrolobserver.h index 214c072cd..71405fabd 100644 --- a/src/views/versioncontrol/versioncontrolobserver.h +++ b/src/views/versioncontrol/versioncontrolobserver.h @@ -51,7 +51,7 @@ class LIBDOLPHINPRIVATE_EXPORT VersionControlObserver : public QObject Q_OBJECT public: - VersionControlObserver(QAbstractItemView* view); + VersionControlObserver(QWidget* parent); virtual ~VersionControlObserver(); QList<QAction*> contextMenuActions(const KFileItemList& items) const; @@ -133,9 +133,9 @@ private: bool m_silentUpdate; // if true, no messages will be send during the update // of version states - QAbstractItemView* m_view; - KDirLister* m_dirLister; - DolphinModel* m_dolphinModel; + QWidget* m_view; + //KDirLister* m_dirLister; + //DolphinModel* m_dolphinModel; QTimer* m_dirVerificationTimer; diff --git a/src/views/viewextensionsfactory.cpp b/src/views/viewextensionsfactory.cpp deleted file mode 100644 index a52871ff4..000000000 --- a/src/views/viewextensionsfactory.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009 by Peter Penz <[email protected]> * - * * - * 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 "viewextensionsfactory.h" - -#include "dolphinfileitemdelegate.h" -#include "dolphinsortfilterproxymodel.h" -#include "dolphinview.h" -#include "dolphinviewcontroller.h" -#include "dolphinviewautoscroller.h" -#include "folderexpander.h" -#include "selectionmanager.h" -#include "settings/dolphinsettings.h" -#include "tooltips/tooltipmanager.h" -#include "versioncontrol/versioncontrolobserver.h" -#include "viewmodecontroller.h" - -#include "dolphin_generalsettings.h" - -#include <KDirLister> -#include <KDirModel> -#include <KFilePreviewGenerator> -#include <QAbstractItemView> -#include <QApplication> - -ViewExtensionsFactory::ViewExtensionsFactory(QAbstractItemView* view, - DolphinViewController* dolphinViewController, - const ViewModeController* viewModeController) : - QObject(view), - m_view(view), - m_dolphinViewController(dolphinViewController), - m_toolTipManager(0), - m_previewGenerator(0), - m_selectionManager(0), - m_autoScroller(0), - m_fileItemDelegate(0), - m_versionControlObserver(0) -{ - view->setSelectionMode(QAbstractItemView::ExtendedSelection); - - GeneralSettings* settings = DolphinSettings::instance().generalSettings(); - - // initialize tooltips - if (settings->showToolTips()) { - DolphinSortFilterProxyModel* proxyModel = static_cast<DolphinSortFilterProxyModel*>(view->model()); - m_toolTipManager = new ToolTipManager(view, proxyModel); - - connect(dolphinViewController, SIGNAL(hideToolTip()), - m_toolTipManager, SLOT(hideToolTip())); - } - - // initialize preview generator - Q_ASSERT(view->iconSize().isValid()); - m_previewGenerator = new KFilePreviewGenerator(view); - m_previewGenerator->setPreviewShown(dolphinViewController->view()->showPreview()); - connect(viewModeController, SIGNAL(zoomLevelChanged(int)), - this, SLOT(slotZoomLevelChanged())); - connect(viewModeController, SIGNAL(cancelPreviews()), - this, SLOT(cancelPreviews())); - - // slotPreviewChanged() is connected as Qt::QueuedConnection to prevent performance - // issues when the directory lister changes its URL after the preview-changes have - // been applied. Usecase: Switch from directory A having no previews to - // directory B with previews (see sequence in DolphinView::setUrl()). - connect(dolphinViewController->view(), SIGNAL(showPreviewChanged()), - this, SLOT(slotShowPreviewChanged()), - Qt::QueuedConnection); - - // initialize selection manager - m_selectionManager = new SelectionManager(view); - connect(m_selectionManager, SIGNAL(selectionChanged()), - this, SLOT(requestActivation())); - connect(viewModeController, SIGNAL(urlChanged(const KUrl&)), - m_selectionManager, SLOT(reset())); - - // initialize auto scroller - m_autoScroller = new DolphinViewAutoScroller(view); - - // initialize file item delegate - m_fileItemDelegate = new DolphinFileItemDelegate(view); - m_fileItemDelegate->setShowToolTipWhenElided(false); - view->setItemDelegate(m_fileItemDelegate); - - // initialize version control observer - const DolphinView* dolphinView = dolphinViewController->view(); - m_versionControlObserver = new VersionControlObserver(view); - connect(m_versionControlObserver, SIGNAL(infoMessage(const QString&)), - dolphinView, SIGNAL(infoMessage(const QString&))); - connect(m_versionControlObserver, SIGNAL(errorMessage(const QString&)), - dolphinView, SIGNAL(errorMessage(const QString&))); - connect(m_versionControlObserver, SIGNAL(operationCompletedMessage(const QString&)), - dolphinView, SIGNAL(operationCompletedMessage(const QString&))); - connect(dolphinViewController, SIGNAL(requestVersionControlActions(const KFileItemList&)), - this, SLOT(slotRequestVersionControlActions(const KFileItemList&))); - - // react on view property changes - connect(dolphinView, SIGNAL(showHiddenFilesChanged()), - this, SLOT(slotShowHiddenFilesChanged())); - connect(dolphinView, SIGNAL(sortingChanged(DolphinView::Sorting)), - this, SLOT(slotSortingChanged(DolphinView::Sorting))); - connect(dolphinView, SIGNAL(sortOrderChanged(Qt::SortOrder)), - this, SLOT(slotSortOrderChanged(Qt::SortOrder))); - connect(dolphinView, SIGNAL(sortFoldersFirstChanged(bool)), - this, SLOT(slotSortFoldersFirstChanged(bool))); - - // Give the view the ability to auto-expand its directories on hovering - // (the column view takes care about this itself). If the details view - // uses expandable folders, the auto-expanding should be used always. - m_folderExpander = new FolderExpander(view, proxyModel()); - m_folderExpander->setEnabled(settings->autoExpandFolders()); - connect(m_folderExpander, SIGNAL(enterDir(const QModelIndex&)), - dolphinViewController, SLOT(triggerItem(const QModelIndex&))); - - // react on namefilter changes - connect(viewModeController, SIGNAL(nameFilterChanged(const QString&)), - this, SLOT(slotNameFilterChanged(const QString&))); - - view->viewport()->installEventFilter(this); -} - -ViewExtensionsFactory::~ViewExtensionsFactory() -{ -} - -void ViewExtensionsFactory::handleCurrentIndexChange(const QModelIndex& current, const QModelIndex& previous) -{ - m_autoScroller->handleCurrentIndexChange(current, previous); -} - -DolphinFileItemDelegate* ViewExtensionsFactory::fileItemDelegate() const -{ - return m_fileItemDelegate; -} - -void ViewExtensionsFactory::setAutoFolderExpandingEnabled(bool enabled) -{ - m_folderExpander->setEnabled(enabled); -} - -bool ViewExtensionsFactory::autoFolderExpandingEnabled() const -{ - return m_folderExpander->enabled(); -} - -bool ViewExtensionsFactory::eventFilter(QObject* watched, QEvent* event) -{ - Q_UNUSED(watched); - if ((event->type() == QEvent::Wheel) && m_selectionManager) { - m_selectionManager->reset(); - } - return false; -} - -void ViewExtensionsFactory::slotZoomLevelChanged() -{ - m_previewGenerator->updateIcons(); - if (m_selectionManager) { - m_selectionManager->reset(); - } -} - -void ViewExtensionsFactory::cancelPreviews() -{ - m_previewGenerator->cancelPreviews(); -} - -void ViewExtensionsFactory::slotShowPreviewChanged() -{ - const bool show = m_dolphinViewController->view()->showPreview(); - m_previewGenerator->setPreviewShown(show); -} - -void ViewExtensionsFactory::slotShowHiddenFilesChanged() -{ - KDirModel* dirModel = static_cast<KDirModel*>(proxyModel()->sourceModel()); - KDirLister* dirLister = dirModel->dirLister(); - - dirLister->stop(); - - const bool show = m_dolphinViewController->view()->showHiddenFiles(); - dirLister->setShowingDotFiles(show); - - const KUrl url = dirLister->url(); - if (url.isValid()) { - dirLister->openUrl(url, KDirLister::NoFlags); - } -} - -void ViewExtensionsFactory::slotSortingChanged(DolphinView::Sorting sorting) -{ - proxyModel()->setSorting(sorting); -} - -void ViewExtensionsFactory::slotSortOrderChanged(Qt::SortOrder order) -{ - proxyModel()->setSortOrder(order); -} - -void ViewExtensionsFactory::slotSortFoldersFirstChanged(bool foldersFirst) -{ - proxyModel()->setSortFoldersFirst(foldersFirst); -} - -void ViewExtensionsFactory::slotNameFilterChanged(const QString& nameFilter) -{ - proxyModel()->setFilterFixedString(nameFilter); -} - -void ViewExtensionsFactory::slotRequestVersionControlActions(const KFileItemList& items) -{ - QList<QAction*> actions; - if (items.isEmpty()) { - const KDirModel* dirModel = static_cast<const KDirModel*>(proxyModel()->sourceModel()); - const KUrl url = dirModel->dirLister()->url(); - actions = m_versionControlObserver->contextMenuActions(url.path(KUrl::AddTrailingSlash)); - } else { - actions = m_versionControlObserver->contextMenuActions(items); - } - m_dolphinViewController->setVersionControlActions(actions); -} - -void ViewExtensionsFactory::requestActivation() -{ - m_dolphinViewController->requestActivation(); -} - -DolphinSortFilterProxyModel* ViewExtensionsFactory::proxyModel() const -{ - return static_cast<DolphinSortFilterProxyModel*>(m_view->model()); -} - -#include "viewextensionsfactory.moc" - diff --git a/src/views/viewextensionsfactory.h b/src/views/viewextensionsfactory.h deleted file mode 100644 index 134868bce..000000000 --- a/src/views/viewextensionsfactory.h +++ /dev/null @@ -1,105 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2009 by Peter Penz <[email protected]> * - * * - * 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 VIEWEXTENSIONSFACTORY_H -#define VIEWEXTENSIONSFACTORY_H - -#include <QObject> - -#include "dolphinview.h" - -class DolphinFileItemDelegate; -class DolphinSortFilterProxyModel; -class DolphinViewAutoScroller; -class KFilePreviewGenerator; -class FolderExpander; -class QModelIndex; -class SelectionManager; -class ToolTipManager; -class QAbstractItemView; -class VersionControlObserver; -class ViewModeController; - -/** - * @brief Responsible for creating extensions like tooltips and previews - * that are available in all view implementations. - * - * Each view implementation (iconsview, detailsview, columnview) must - * instantiate an instance of this class to assure having - * a common behavior that is independent from the custom functionality of - * a view implementation. - */ -class ViewExtensionsFactory : public QObject -{ - Q_OBJECT - -public: - explicit ViewExtensionsFactory(QAbstractItemView* view, - DolphinViewController* dolphinViewController, - const ViewModeController* viewModeController); - virtual ~ViewExtensionsFactory(); - - /** - * Must be invoked by the item view, when QAbstractItemView::currentChanged() - * has been called. Assures that the current item stays visible when it has been - * changed by the keyboard. - */ - void handleCurrentIndexChange(const QModelIndex& current, const QModelIndex& previous); - - DolphinFileItemDelegate* fileItemDelegate() const; - - /** - * Enables the automatically expanding of a folder when dragging - * items above the folder. - */ - void setAutoFolderExpandingEnabled(bool enabled); - bool autoFolderExpandingEnabled() const; - -protected: - virtual bool eventFilter(QObject* watched, QEvent* event); - -private slots: - void slotZoomLevelChanged(); - void cancelPreviews(); - void slotShowPreviewChanged(); - void slotShowHiddenFilesChanged(); - void slotSortingChanged(DolphinView::Sorting sorting); - void slotSortOrderChanged(Qt::SortOrder order); - void slotSortFoldersFirstChanged(bool foldersFirst); - void slotNameFilterChanged(const QString& nameFilter); - void slotRequestVersionControlActions(const KFileItemList& items); - void requestActivation(); - -private: - DolphinSortFilterProxyModel* proxyModel() const; - -private: - QAbstractItemView* m_view; - DolphinViewController* m_dolphinViewController; - ToolTipManager* m_toolTipManager; - KFilePreviewGenerator* m_previewGenerator; - SelectionManager* m_selectionManager; - DolphinViewAutoScroller* m_autoScroller; - DolphinFileItemDelegate* m_fileItemDelegate; - VersionControlObserver* m_versionControlObserver; - FolderExpander* m_folderExpander; -}; - -#endif - diff --git a/src/views/viewproperties.cpp b/src/views/viewproperties.cpp index d0c6fcf1e..aeea67428 100644 --- a/src/views/viewproperties.cpp +++ b/src/views/viewproperties.cpp @@ -38,7 +38,7 @@ namespace { // String representation to mark the additional properties of // the details view as customized by the user. See - // ViewProperties::additionalInfoV2() for more information. + // ViewProperties::additionalInfoList() for more information. const char* CustomizedDetailsString = "CustomizedDetails"; } @@ -83,7 +83,7 @@ ViewProperties::ViewProperties(const KUrl& url) : if (useDefaultProps) { if (useDetailsViewWithPath) { setViewMode(DolphinView::DetailsView); - setAdditionalInfo(KFileItemDelegate::InformationList() << KFileItemDelegate::LocalPathOrUrl); + setAdditionalInfoList(QList<DolphinView::AdditionalInfo>() << DolphinView::PathInfo); } else { // The global view-properties act as default for directories without // any view-property configuration @@ -121,23 +121,23 @@ DolphinView::Mode ViewProperties::viewMode() const return static_cast<DolphinView::Mode>(m_node->viewMode()); } -void ViewProperties::setShowPreview(bool show) +void ViewProperties::setPreviewsShown(bool show) { - if (m_node->showPreview() != show) { - m_node->setShowPreview(show); + if (m_node->previewsShown() != show) { + m_node->setPreviewsShown(show); update(); } } -bool ViewProperties::showPreview() const +bool ViewProperties::previewsShown() const { - return m_node->showPreview(); + return m_node->previewsShown(); } -void ViewProperties::setShowHiddenFiles(bool show) +void ViewProperties::setHiddenFilesShown(bool show) { - if (m_node->showHiddenFiles() != show) { - m_node->setShowHiddenFiles(show); + if (m_node->hiddenFilesShown() != show) { + m_node->setHiddenFilesShown(show); update(); } } @@ -155,9 +155,9 @@ bool ViewProperties::categorizedSorting() const return m_node->categorizedSorting(); } -bool ViewProperties::showHiddenFiles() const +bool ViewProperties::hiddenFilesShown() const { - return m_node->showHiddenFiles(); + return m_node->hiddenFilesShown(); } void ViewProperties::setSorting(DolphinView::Sorting sorting) @@ -199,13 +199,13 @@ bool ViewProperties::sortFoldersFirst() const return m_node->sortFoldersFirst(); } -void ViewProperties::setAdditionalInfo(const KFileItemDelegate::InformationList& list) +void ViewProperties::setAdditionalInfoList(const QList<DolphinView::AdditionalInfo>& list) { - // See ViewProperties::additionalInfoV2() for the storage format + // See ViewProperties::additionalInfoList() for the storage format // of the additional information. // Remove the old values stored for the current view-mode - const QStringList oldInfoStringList = m_node->additionalInfoV2(); + const QStringList oldInfoStringList = m_node->additionalInfo(); const QString prefix = viewModePrefix(); QStringList newInfoStringList = oldInfoStringList; for (int i = newInfoStringList.count() - 1; i >= 0; --i) { @@ -216,7 +216,7 @@ void ViewProperties::setAdditionalInfo(const KFileItemDelegate::InformationList& // Add the updated values for the current view-mode AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); - foreach (KFileItemDelegate::Information info, list) { + foreach (DolphinView::AdditionalInfo info, list) { newInfoStringList.append(prefix + infoAccessor.value(info)); } @@ -232,10 +232,6 @@ void ViewProperties::setAdditionalInfo(const KFileItemDelegate::InformationList& } if (changed) { - if (m_node->version() < 2) { - m_node->setVersion(2); - } - const bool markCustomizedDetails = (m_node->viewMode() == DolphinView::DetailsView) && !newInfoStringList.contains(CustomizedDetailsString); if (markCustomizedDetails) { @@ -246,35 +242,80 @@ void ViewProperties::setAdditionalInfo(const KFileItemDelegate::InformationList& newInfoStringList.append(CustomizedDetailsString); } - m_node->setAdditionalInfoV2(newInfoStringList); + m_node->setAdditionalInfo(newInfoStringList); update(); } } -KFileItemDelegate::InformationList ViewProperties::additionalInfo() const +QList<DolphinView::AdditionalInfo> ViewProperties::additionalInfoList() const { - KFileItemDelegate::InformationList usedInfo; + // The shown additional information is stored for each view-mode separately as + // string with the view-mode as prefix. Example: + // + // AdditionalInfo=Details_Size,Details_Date,Details_Owner,Icon_Size + // + // To get the representation as QList<DolphinView::AdditionalInfo>, the current + // view-mode must be checked and the values of this mode added to the list. + // + // For the details-view a special case must be respected: Per default the size + // and date should be shown without creating a .directory file. Only if + // the user explictly has modified the properties of the details view (marked + // by "CustomizedDetails"), also a details-view with no additional information + // is accepted. - switch (m_node->version()) { - case 1: usedInfo = additionalInfoV1(); break; - case 2: usedInfo = additionalInfoV2(); break; - default: kWarning() << "Unknown version of the view properties"; + QList<DolphinView::AdditionalInfo> usedInfo; + + // infoHash allows to get the mapped DolphinView::AdditionalInfo value + // for a stored string-value in a fast way + static QHash<QString, DolphinView::AdditionalInfo> infoHash; + if (infoHash.isEmpty()) { + AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); + const QList<DolphinView::AdditionalInfo> keys = infoAccessor.keys(); + foreach (DolphinView::AdditionalInfo key, keys) { + infoHash.insert(infoAccessor.value(key), key); + } + } + + // Iterate through all stored keys stored as strings and map them to + // the corresponding DolphinView::AdditionalInfo values. + const QString prefix = viewModePrefix(); + const int prefixLength = prefix.length(); + const QStringList infoStringList = m_node->additionalInfo(); + foreach (const QString& infoString, infoStringList) { + if (infoString.startsWith(prefix)) { + const QString key = infoString.right(infoString.length() - prefixLength); + if (infoHash.contains(key)) { + usedInfo.append(infoHash.value(key)); + } else { + kWarning() << "Did not find the key" << key << "in the information string"; + } + } + } + + // For the details view the size and date should be shown per default + // until the additional information has been explicitly changed by the user + const bool useDefaultValues = usedInfo.isEmpty() + && (m_node->viewMode() == DolphinView::DetailsView) + && !infoStringList.contains(CustomizedDetailsString); + Q_UNUSED(useDefaultValues); + if (useDefaultValues) { + usedInfo.append(DolphinView::SizeInfo); + usedInfo.append(DolphinView::DateInfo); } return usedInfo; } - void ViewProperties::setDirProperties(const ViewProperties& props) { setViewMode(props.viewMode()); - setShowPreview(props.showPreview()); - setShowHiddenFiles(props.showHiddenFiles()); + setPreviewsShown(props.previewsShown()); + setHiddenFilesShown(props.hiddenFilesShown()); setCategorizedSorting(props.categorizedSorting()); setSorting(props.sorting()); setSortOrder(props.sortOrder()); setSortFoldersFirst(props.sortFoldersFirst()); - setAdditionalInfo(props.additionalInfo()); + setAdditionalInfoList(props.additionalInfoList()); } void ViewProperties::setAutoSaveEnabled(bool autoSave) @@ -291,22 +332,6 @@ void ViewProperties::update() { m_changedProps = true; m_node->setTimestamp(QDateTime::currentDateTime()); - - // If the view-properties are stored in an older format, take - // care to update them to the current format. - switch (m_node->version()) { - case 1: { - const KFileItemDelegate::InformationList infoList = additionalInfoV1(); - m_node->setVersion(2); - setAdditionalInfo(infoList); - break; - } - case 2: - // Current version. Nothing needs to get converted. - break; - default: - kWarning() << "Unknown version of the view properties"; - } } void ViewProperties::save() @@ -330,107 +355,14 @@ QString ViewProperties::destinationDir(const QString& subDir) const return KStandardDirs::locateLocal("data", basePath); } -KFileItemDelegate::InformationList ViewProperties::additionalInfoV1() const -{ - KFileItemDelegate::InformationList usedInfo; - - int decodedInfo = m_node->additionalInfo(); - - switch (viewMode()) { - case DolphinView::DetailsView: - decodedInfo = decodedInfo & 0xFF; - if (decodedInfo == 0) { - // A details view without any additional info makes no sense, hence - // provide at least a size-info and date-info as fallback - AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); - decodedInfo = infoAccessor.bitValue(KFileItemDelegate::Size) | - infoAccessor.bitValue(KFileItemDelegate::ModificationTime); - } - break; - case DolphinView::IconsView: - decodedInfo = (decodedInfo >> 8) & 0xFF; - break; - case DolphinView::ColumnView: - decodedInfo = (decodedInfo >> 16) & 0xFF; - break; - default: break; - } - - AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); - const KFileItemDelegate::InformationList infoKeys = infoAccessor.keys(); - - foreach (const KFileItemDelegate::Information info, infoKeys) { - if (decodedInfo & infoAccessor.bitValue(info)) { - usedInfo.append(info); - } - } - - return usedInfo; -} - -KFileItemDelegate::InformationList ViewProperties::additionalInfoV2() const -{ - // The shown additional information is stored for each view-mode separately as - // string with the view-mode as prefix. Example: - // - // AdditionalInfoV2=Details_Size,Details_Date,Details_Owner,Icon_Size - // - // To get the representation as KFileItemDelegate::InformationList, the current - // view-mode must be checked and the values of this mode added to the list. - // - // For the details-view a special case must be respected: Per default the size - // and date should be shown without creating a .directory file. Only if - // the user explictly has modified the properties of the details view (marked - // by "CustomizedDetails"), also a details-view with no additional information - // is accepted. - - KFileItemDelegate::InformationList usedInfo; - - // infoHash allows to get the mapped KFileItemDelegate::Information value - // for a stored string-value in a fast way - static QHash<QString, KFileItemDelegate::Information> infoHash; - if (infoHash.isEmpty()) { - AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); - const KFileItemDelegate::InformationList keys = infoAccessor.keys(); - foreach (const KFileItemDelegate::Information key, keys) { - infoHash.insert(infoAccessor.value(key), key); - } - } - - // Iterate through all stored keys stored as strings and map them to - // the corresponding KFileItemDelegate::Information values. - const QString prefix = viewModePrefix(); - const int prefixLength = prefix.length(); - const QStringList infoStringList = m_node->additionalInfoV2(); - foreach (const QString& infoString, infoStringList) { - if (infoString.startsWith(prefix)) { - const QString key = infoString.right(infoString.length() - prefixLength); - Q_ASSERT(infoHash.contains(key)); - usedInfo.append(infoHash.value(key)); - } - } - - // For the details view the size and date should be shown per default - // until the additional information has been explicitly changed by the user - const bool useDefaultValues = usedInfo.isEmpty() - && (m_node->viewMode() == DolphinView::DetailsView) - && !infoStringList.contains(CustomizedDetailsString); - if (useDefaultValues) { - usedInfo.append(KFileItemDelegate::Size); - usedInfo.append(KFileItemDelegate::ModificationTime); - } - - return usedInfo; -} - QString ViewProperties::viewModePrefix() const { QString prefix; switch (m_node->viewMode()) { - case DolphinView::DetailsView: prefix = "Details_"; break; case DolphinView::IconsView: prefix = "Icons_"; break; - case DolphinView::ColumnView: prefix = "Column_"; break; + case DolphinView::CompactView: prefix = "Compact_"; break; + case DolphinView::DetailsView: prefix = "Details_"; break; default: kWarning() << "Unknown view-mode of the view properties"; } diff --git a/src/views/viewproperties.h b/src/views/viewproperties.h index ac373d677..c95134a15 100644 --- a/src/views/viewproperties.h +++ b/src/views/viewproperties.h @@ -37,7 +37,7 @@ class ViewPropertySettings; * \code * ViewProperties props(KUrl("/home/peter/Documents")); * const DolphinView::Mode mode = props.viewMode(); - * const bool showHiddenFiles = props.isShowHiddenFilesEnabled(); + * const bool hiddenFilesShown = props.hiddenFilesShown(); * \endcode * * When modifying a view property, the '.directory' file is automatically updated @@ -56,11 +56,11 @@ public: void setViewMode(DolphinView::Mode mode); DolphinView::Mode viewMode() const; - void setShowPreview(bool show); - bool showPreview() const; + void setPreviewsShown(bool show); + bool previewsShown() const; - void setShowHiddenFiles(bool show); - bool showHiddenFiles() const; + void setHiddenFilesShown(bool show); + bool hiddenFilesShown() const; void setCategorizedSorting(bool categorized); bool categorizedSorting() const; @@ -79,14 +79,14 @@ public: * Note that the additional-info property is the only property where * the value is dependent from another property (in this case the view-mode). */ - void setAdditionalInfo(const KFileItemDelegate::InformationList& info); + void setAdditionalInfoList(const QList<DolphinView::AdditionalInfo>& info); /** * Returns the additional information for the current set view-mode. * Note that the additional-info property is the only property where * the value is dependent from another property (in this case the view-mode). */ - KFileItemDelegate::InformationList additionalInfo() const; + QList<DolphinView::AdditionalInfo> additionalInfoList() const; /** * Sets the directory properties view mode, show preview, @@ -132,18 +132,6 @@ private: QString destinationDir(const QString& subDir) const; /** - * Helper method for ViewProperties::additionalInfo(): Returns - * the additional info for the outdated version 1 of the view-properties. - */ - KFileItemDelegate::InformationList additionalInfoV1() const; - - /** - * Helper method for ViewProperties::additionalInfo(): Returns - * the additional info for the current version 2 of the view-properties. - */ - KFileItemDelegate::InformationList additionalInfoV2() const; - - /** * Returns the view-mode prefix when storing additional properties for * a view-mode. */ |
