diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bc880bb67..314c6cb28 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,291 +1,301 @@ macro_optional_find_package(Nepomuk) macro_log_feature(Nepomuk_FOUND "Nepomuk" "Nepomuk" "http://www.kde.org" FALSE "" "For adding desktop-wide tagging support to dolphin") 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) # Yes, Soprano includes is what we need here include_directories( ${SOPRANO_INCLUDE_DIR} ${NEPOMUK_INCLUDE_DIR} ) endif (Nepomuk_FOUND) 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 settings/dolphin_generalsettings.kcfgc settings/dolphin_versioncontrolsettings.kcfgc ) kde4_add_library(dolphinprivate SHARED ${dolphinprivate_LIB_SRCS}) target_link_libraries(dolphinprivate ${KDE4_KFILE_LIBS} konq ${KDE4_KNEWSTUFF3_LIBS}) 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} ) install(TARGETS dolphinprivate ${INSTALL_TARGETS_DEFAULT_ARGS}) ########################################## set(dolphinpart_SRCS dolphinpart.cpp ) kde4_add_plugin(dolphinpart ${dolphinpart_SRCS}) target_link_libraries(dolphinpart dolphinprivate konq ${KDE4_KPARTS_LIBS} ${KDE4_KFILE_LIBS}) install(TARGETS dolphinpart DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES dolphinpart.rc DESTINATION ${DATA_INSTALL_DIR}/dolphinpart) install(FILES dolphinpart.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) install(FILES views/versioncontrol/fileviewversioncontrolplugin.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) ########################################## set(dolphin_SRCS dolphinapplication.cpp dolphindockwidget.cpp dolphinmainwindow.cpp dolphinnewfilemenu.cpp dolphinviewcontainer.cpp dolphincontextmenu.cpp filterbar/filterbar.cpp main.cpp panels/information/filemetadataconfigurationdialog.cpp panels/information/informationpanel.cpp panels/information/informationpanelcontent.cpp panels/information/pixmapviewer.cpp panels/information/phononwidget.cpp panels/folders/ktreeview.cpp panels/places/placespanel.cpp panels/panel.cpp panels/folders/treeviewcontextmenu.cpp panels/folders/folderspanel.cpp panels/folders/paneltreeview.cpp search/dolphinsearchbox.cpp search/dolphinsearchinformation.cpp settings/general/behaviorsettingspage.cpp settings/general/configurepreviewplugindialog.cpp settings/general/contextmenusettingspage.cpp settings/general/generalsettingspage.cpp settings/general/previewssettingspage.cpp settings/general/statusbarsettingspage.cpp settings/dolphinsettingsdialog.cpp settings/navigation/navigationsettingspage.cpp settings/services/servicessettingspage.cpp settings/settingspagebase.cpp settings/serviceitemdelegate.cpp 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 settings/viewmodes/iconsviewsettingspage.cpp settings/viewmodes/viewsettingspage.cpp settings/viewmodes/viewsettingspagebase.cpp statusbar/dolphinstatusbar.cpp statusbar/statusbarspaceinfo.cpp views/zoomlevelinfo.cpp ) 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 ) if(Nepomuk_FOUND) set(dolphin_SRCS ${dolphin_SRCS} panels/search/searchpanel.cpp ) endif(Nepomuk_FOUND) if(NOT WIN32) set(dolphin_SRCS ${dolphin_SRCS} panels/terminal/terminalpanel.cpp) endif(NOT WIN32) kde4_add_app_icon(dolphin_SRCS "${KDE4_ICON_INSTALL_DIR}/oxygen/*/apps/system-file-manager.png") kde4_add_kdeinit_executable(dolphin ${dolphin_SRCS}) target_link_libraries(kdeinit_dolphin ${KDE4_KDEPRINT_LIBS} ${KDE4_KFILE_LIBS} ${KDE4_KPARTS_LIBS} ${KDE4_KCMUTILS_LIBRARY} konq dolphinprivate knewstuff3 ${KDE4_PHONON_LIBS} ) if (Nepomuk_FOUND) target_link_libraries(kdeinit_dolphin ${NEPOMUK_LIBRARIES} ${SOPRANO_LIBRARIES} ${NEPOMUK_QUERY_LIBRARIES} nepomukutils ) endif (Nepomuk_FOUND) install(TARGETS kdeinit_dolphin ${INSTALL_TARGETS_DEFAULT_ARGS}) 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 settings/viewmodes/iconsviewsettingspage.cpp settings/viewmodes/viewsettingspagebase.cpp views/zoomlevelinfo.cpp) set(kcm_dolphinnavigation_PART_SRCS settings/kcm/kcmdolphinnavigation.cpp settings/navigation/navigationsettingspage.cpp settings/settingspagebase.cpp) set(kcm_dolphinservices_PART_SRCS settings/kcm/kcmdolphinservices.cpp settings/services/servicessettingspage.cpp settings/settingspagebase.cpp settings/serviceitemdelegate.cpp settings/servicemodel.cpp) set(kcm_dolphingeneral_PART_SRCS settings/kcm/kcmdolphingeneral.cpp settings/general/behaviorsettingspage.cpp settings/general/previewssettingspage.cpp settings/general/configurepreviewplugindialog.cpp settings/general/contextmenusettingspage.cpp settings/settingspagebase.cpp settings/serviceitemdelegate.cpp 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 settings/dolphin_generalsettings.kcfgc settings/dolphin_versioncontrolsettings.kcfgc ) kde4_add_kcfg_files(kcm_dolphinnavigation_PART_SRCS settings/dolphin_generalsettings.kcfgc) kde4_add_kcfg_files(kcm_dolphinservices_PART_SRCS settings/dolphin_generalsettings.kcfgc settings/dolphin_versioncontrolsettings.kcfgc) kde4_add_kcfg_files(kcm_dolphingeneral_PART_SRCS settings/dolphin_generalsettings.kcfgc) kde4_add_plugin(kcm_dolphinviewmodes ${kcm_dolphinviewmodes_PART_SRCS}) kde4_add_plugin(kcm_dolphinnavigation ${kcm_dolphinnavigation_PART_SRCS}) kde4_add_plugin(kcm_dolphinservices ${kcm_dolphinservices_PART_SRCS}) kde4_add_plugin(kcm_dolphingeneral ${kcm_dolphingeneral_PART_SRCS}) target_link_libraries(kcm_dolphinviewmodes ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS} dolphinprivate) target_link_libraries(kcm_dolphinnavigation ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS} dolphinprivate) target_link_libraries(kcm_dolphinservices ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS} ${KDE4_KIO_LIBS} ${KDE4_KNEWSTUFF3_LIBRARY} dolphinprivate) target_link_libraries(kcm_dolphingeneral ${KDE4_KDEUI_LIBS} ${KDE4_KFILE_LIBS} ${KDE4_KIO_LIBS} dolphinprivate) if (Nepomuk_FOUND) target_link_libraries(kcm_dolphinviewmodes ${NEPOMUK_LIBRARIES}) target_link_libraries(kcm_dolphinnavigation ${NEPOMUK_LIBRARIES}) target_link_libraries(kcm_dolphinservices ${NEPOMUK_LIBRARIES}) target_link_libraries(kcm_dolphingeneral ${NEPOMUK_LIBRARIES}) endif (Nepomuk_FOUND) install(TARGETS kcm_dolphinviewmodes DESTINATION ${PLUGIN_INSTALL_DIR} ) install(TARGETS kcm_dolphinnavigation DESTINATION ${PLUGIN_INSTALL_DIR} ) install(TARGETS kcm_dolphinservices DESTINATION ${PLUGIN_INSTALL_DIR} ) install(TARGETS kcm_dolphingeneral DESTINATION ${PLUGIN_INSTALL_DIR} ) ######################################### set(kio_search_PART_SRCS search/filenamesearchprotocol.cpp) kde4_add_plugin(kio_filenamesearch ${kio_search_PART_SRCS}) target_link_libraries(kio_filenamesearch ${KDE4_KIO_LIBS}) install(TARGETS kio_filenamesearch DESTINATION ${PLUGIN_INSTALL_DIR}) ########### install files ############### 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 DESTINATION ${KCFG_INSTALL_DIR} ) install( FILES dolphinui.rc DESTINATION ${DATA_INSTALL_DIR}/dolphin ) install( FILES search/filenamesearch.protocol DESTINATION ${SERVICES_INSTALL_DIR} ) install( FILES settings/kcm/kcmdolphinviewmodes.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) install( FILES settings/kcm/kcmdolphinnavigation.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) install( FILES settings/kcm/kcmdolphinservices.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) install( FILES settings/kcm/kcmdolphingeneral.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) install( FILES settings/services/servicemenu.knsrc DESTINATION ${CONFIG_INSTALL_DIR} ) install( PROGRAMS settings/services/servicemenuinstallation DESTINATION ${BIN_INSTALL_DIR} ) install( PROGRAMS settings/services/servicemenudeinstallation DESTINATION ${BIN_INSTALL_DIR} ) 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 @@ -1,558 +1,558 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at) and * * Cvetoslav Ludmiloff * * * * 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 "dolphincontextmenu.h" #include "dolphinmainwindow.h" #include "dolphinnewfilemenu.h" #include "settings/dolphinsettings.h" #include "dolphinviewcontainer.h" #include "dolphin_generalsettings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "views/dolphinview.h" #include "views/viewmodecontroller.h" K_GLOBAL_STATIC(KModifierKeyInfo, m_keyInfo) DolphinContextMenu::DolphinContextMenu(DolphinMainWindow* parent, const KFileItem& fileInfo, const KUrl& baseUrl) : m_mainWindow(parent), m_fileInfo(fileInfo), m_baseUrl(baseUrl), m_baseFileItem(0), m_selectedItems(), m_selectedItemsProperties(0), m_context(NoContext), m_copyToMenu(parent), m_customActions(), m_popup(0), m_command(None), m_shiftPressed(false), m_removeAction(0) { // The context menu either accesses the URLs of the selected items // or the items itself. To increase the performance both lists are cached. const DolphinView* view = m_mainWindow->activeViewContainer()->view(); m_selectedItems = view->selectedItems(); if (m_keyInfo) { if (m_keyInfo->isKeyPressed(Qt::Key_Shift) || m_keyInfo->isKeyLatched(Qt::Key_Shift)) { m_shiftPressed = true; } connect(m_keyInfo, SIGNAL(keyPressed(Qt::Key, bool)), this, SLOT(slotKeyModifierPressed(Qt::Key, bool))); } m_removeAction = new QAction(this); connect(m_removeAction, SIGNAL(triggered()), this, SLOT(slotRemoveActionTriggered())); m_popup = new KMenu(m_mainWindow); } DolphinContextMenu::~DolphinContextMenu() { delete m_selectedItemsProperties; m_selectedItemsProperties = 0; delete m_popup; m_popup = 0; } void DolphinContextMenu::setCustomActions(const QList& actions) { m_customActions = actions; } DolphinContextMenu::Command DolphinContextMenu::open() { // get the context information if (m_baseUrl.protocol() == QLatin1String("trash")) { m_context |= TrashContext; } if (!m_fileInfo.isNull() && !m_selectedItems.isEmpty()) { m_context |= ItemContext; // TODO: handle other use cases like devices + desktop files } // open the corresponding popup for the context if (m_context & TrashContext) { if (m_context & ItemContext) { openTrashItemContextMenu(); } else { openTrashContextMenu(); } } else if (m_context & ItemContext) { openItemContextMenu(); } else { Q_ASSERT(m_context == NoContext); openViewportContextMenu(); } return m_command; } void DolphinContextMenu::initializeModifierKeyInfo() { // Access m_keyInfo, so that it gets instantiated by // K_GLOBAL_STATIC KModifierKeyInfo* keyInfo = m_keyInfo; Q_UNUSED(keyInfo); } void DolphinContextMenu::slotKeyModifierPressed(Qt::Key key, bool pressed) { m_shiftPressed = (key == Qt::Key_Shift) && pressed; updateRemoveAction(); } void DolphinContextMenu::slotRemoveActionTriggered() { const KActionCollection* collection = m_mainWindow->actionCollection(); if (m_shiftPressed) { collection->action("delete")->trigger(); } else { collection->action("move_to_trash")->trigger(); } } void DolphinContextMenu::openTrashContextMenu() { Q_ASSERT(m_context & TrashContext); QAction* emptyTrashAction = new QAction(KIcon("trash-empty"), i18nc("@action:inmenu", "Empty Trash"), m_popup); KConfig trashConfig("trashrc", KConfig::SimpleConfig); emptyTrashAction->setEnabled(!trashConfig.group("Status").readEntry("Empty", true)); m_popup->addAction(emptyTrashAction); QAction* addToPlacesAction = m_popup->addAction(KIcon("bookmark-new"), i18nc("@action:inmenu Add current folder to places", "Add to Places")); // Don't show if url is already in places if (placeExists(m_mainWindow->activeViewContainer()->url())) { addToPlacesAction->setVisible(false); } addCustomActions(); QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties"); m_popup->addAction(propertiesAction); addShowMenuBarAction(); QAction *action = m_popup->exec(QCursor::pos()); if (action == emptyTrashAction) { KonqOperations::emptyTrash(m_mainWindow); } else if (action == addToPlacesAction) { const KUrl& url = m_mainWindow->activeViewContainer()->url(); if (url.isValid()) { DolphinSettings::instance().placesModel()->addPlace(i18nc("@label", "Trash"), url); } } } void DolphinContextMenu::openTrashItemContextMenu() { Q_ASSERT(m_context & TrashContext); Q_ASSERT(m_context & ItemContext); QAction* restoreAction = new QAction(i18nc("@action:inmenu", "Restore"), m_mainWindow); m_popup->addAction(restoreAction); QAction* deleteAction = m_mainWindow->actionCollection()->action("delete"); m_popup->addAction(deleteAction); QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties"); m_popup->addAction(propertiesAction); if (m_popup->exec(QCursor::pos()) == restoreAction) { KUrl::List selectedUrls; foreach (const KFileItem &item, m_selectedItems) { selectedUrls.append(item.url()); } KonqOperations::restoreTrashedItems(selectedUrls, m_mainWindow); } } void DolphinContextMenu::openItemContextMenu() { Q_ASSERT(!m_fileInfo.isNull()); QAction* openParentInNewWindowAction = 0; QAction* openParentInNewTabAction = 0; QAction* addToPlacesAction = 0; if (m_selectedItems.count() == 1) { if (m_fileInfo.isDir()) { // 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()); connect(newFileMenu, SIGNAL(fileCreated(KUrl)), newFileMenu, SLOT(deleteLater())); connect(newFileMenu, SIGNAL(directoryCreated(KUrl)), newFileMenu, SLOT(deleteLater())); KMenu* menu = newFileMenu->menu(); menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New")); menu->setIcon(KIcon("document-new")); m_popup->addMenu(menu); m_popup->addSeparator(); // insert 'Open in new window' and 'Open in new tab' entries m_popup->addAction(m_mainWindow->actionCollection()->action("open_in_new_window")); m_popup->addAction(m_mainWindow->actionCollection()->action("open_in_new_tab")); // insert 'Add to Places' entry if (!placeExists(m_fileInfo.url())) { addToPlacesAction = m_popup->addAction(KIcon("bookmark-new"), i18nc("@action:inmenu Add selected folder to places", "Add to Places")); } m_popup->addSeparator(); } else if (m_baseUrl.protocol().contains("search")) { openParentInNewWindowAction = new QAction(KIcon("window-new"), i18nc("@action:inmenu", "Open Path in New Window"), this); m_popup->addAction(openParentInNewWindowAction); openParentInNewTabAction = new QAction(KIcon("tab-new"), i18nc("@action:inmenu", "Open Path in New Tab"), this); m_popup->addAction(openParentInNewTabAction); m_popup->addSeparator(); } } insertDefaultItemActions(); m_popup->addSeparator(); KFileItemActions fileItemActions; fileItemActions.setItemListProperties(selectedItemsProperties()); addServiceActions(fileItemActions); addFileItemPluginActions(); addVersionControlPluginActions(); // insert 'Copy To' and 'Move To' sub menus if (DolphinSettings::instance().generalSettings()->showCopyMoveMenu()) { m_copyToMenu.setItems(m_selectedItems); m_copyToMenu.setReadOnly(!selectedItemsProperties().supportsWriting()); m_copyToMenu.addActionsTo(m_popup); } // insert 'Properties...' entry QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties"); m_popup->addAction(propertiesAction); QAction* activatedAction = m_popup->exec(QCursor::pos()); if (activatedAction) { if (activatedAction == addToPlacesAction) { const KUrl selectedUrl(m_fileInfo.url()); if (selectedUrl.isValid()) { DolphinSettings::instance().placesModel()->addPlace(placesName(selectedUrl), selectedUrl); } } else if (activatedAction == openParentInNewWindowAction) { m_command = OpenParentFolderInNewWindow; } else if (activatedAction == openParentInNewTabAction) { m_command = OpenParentFolderInNewTab; } } } 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()); m_popup->addSeparator(); // Insert 'New Window' and 'New Tab' entries. Don't use "open_in_new_window" and // "open_in_new_tab" here, as the current selection should get ignored. m_popup->addAction(m_mainWindow->actionCollection()->action("new_window")); m_popup->addAction(m_mainWindow->actionCollection()->action("new_tab")); // Insert 'Add to Places' entry if exactly one item is selected QAction* addToPlacesAction = 0; if (!placeExists(m_mainWindow->activeViewContainer()->url())) { addToPlacesAction = m_popup->addAction(KIcon("bookmark-new"), i18nc("@action:inmenu Add current folder to places", "Add to Places")); } m_popup->addSeparator(); QAction* pasteAction = createPasteAction(); m_popup->addAction(pasteAction); m_popup->addSeparator(); // Insert service actions const KFileItemListProperties baseUrlProperties(KFileItemList() << baseFileItem()); KFileItemActions fileItemActions; fileItemActions.setItemListProperties(baseUrlProperties); addServiceActions(fileItemActions); addFileItemPluginActions(); addVersionControlPluginActions(); addCustomActions(); QAction* propertiesAction = m_mainWindow->actionCollection()->action("properties"); m_popup->addAction(propertiesAction); addShowMenuBarAction(); QAction* action = m_popup->exec(QCursor::pos()); if (addToPlacesAction && (action == addToPlacesAction)) { const KUrl url = m_mainWindow->activeViewContainer()->url(); if (url.isValid()) { DolphinSettings::instance().placesModel()->addPlace(placesName(url), url); } } } void DolphinContextMenu::insertDefaultItemActions() { const KActionCollection* collection = m_mainWindow->actionCollection(); // Insert 'Cut', 'Copy' and 'Paste' m_popup->addAction(collection->action(KStandardAction::name(KStandardAction::Cut))); m_popup->addAction(collection->action(KStandardAction::name(KStandardAction::Copy))); m_popup->addAction(createPasteAction()); m_popup->addSeparator(); // Insert 'Rename' QAction* renameAction = collection->action("rename"); m_popup->addAction(renameAction); // Insert 'Move to Trash' and/or 'Delete' if (KGlobal::config()->group("KDE").readEntry("ShowDeleteCommand", false)) { m_popup->addAction(collection->action("move_to_trash")); m_popup->addAction(collection->action("delete")); } else { m_popup->addAction(m_removeAction); updateRemoveAction(); } } void DolphinContextMenu::addShowMenuBarAction() { const KActionCollection* ac = m_mainWindow->actionCollection(); QAction* showMenuBar = ac->action(KStandardAction::name(KStandardAction::ShowMenubar)); if (!m_mainWindow->menuBar()->isVisible() && !m_mainWindow->toolBar()->isVisible()) { m_popup->addSeparator(); m_popup->addAction(showMenuBar); } } QString DolphinContextMenu::placesName(const KUrl& url) const { QString name = url.fileName(); if (name.isEmpty()) { name = url.host(); } return name; } bool DolphinContextMenu::placeExists(const KUrl& url) const { const KFilePlacesModel* placesModel = DolphinSettings::instance().placesModel(); const int count = placesModel->rowCount(); for (int i = 0; i < count; ++i) { const QModelIndex index = placesModel->index(i, 0); if (url.equals(placesModel->url(index), KUrl::CompareWithoutTrailingSlash)) { return true; } } return false; } QAction* DolphinContextMenu::createPasteAction() { QAction* action = 0; const bool isDir = !m_fileInfo.isNull() && m_fileInfo.isDir(); if (isDir && (m_selectedItems.count() == 1)) { action = new QAction(KIcon("edit-paste"), i18nc("@action:inmenu", "Paste Into Folder"), this); const QMimeData* mimeData = QApplication::clipboard()->mimeData(); const KUrl::List pasteData = KUrl::List::fromMimeData(mimeData); action->setEnabled(!pasteData.isEmpty() && selectedItemsProperties().supportsWriting()); connect(action, SIGNAL(triggered()), m_mainWindow, SLOT(pasteIntoFolder())); } else { action = m_mainWindow->actionCollection()->action(KStandardAction::name(KStandardAction::Paste)); } return action; } KFileItemListProperties& DolphinContextMenu::selectedItemsProperties() { if (!m_selectedItemsProperties) { m_selectedItemsProperties = new KFileItemListProperties(m_selectedItems); } return *m_selectedItemsProperties; } KFileItem DolphinContextMenu::baseFileItem() { if (!m_baseFileItem) { m_baseFileItem = new KFileItem(KFileItem::Unknown, KFileItem::Unknown, m_baseUrl); } return *m_baseFileItem; } void DolphinContextMenu::addServiceActions(KFileItemActions& fileItemActions) { fileItemActions.setParentWidget(m_mainWindow); // insert 'Open With...' action or sub menu fileItemActions.addOpenWithActionsTo(m_popup, "DesktopEntryName != 'dolphin'"); // insert 'Actions' sub menu fileItemActions.addServiceActionsTo(m_popup); } void DolphinContextMenu::addFileItemPluginActions() { KFileItemListProperties props; if (m_selectedItems.isEmpty()) { props.setItems(KFileItemList() << baseFileItem()); } else { props = selectedItemsProperties(); } QString commonMimeType = props.mimeType(); if (commonMimeType.isEmpty()) { commonMimeType = QLatin1String("application/octet-stream"); } const KService::List pluginServices = KMimeTypeTrader::self()->query(commonMimeType, "KFileItemAction/Plugin", "exist Library"); if (pluginServices.isEmpty()) { return; } const KConfig config("kservicemenurc", KConfig::NoGlobals); const KConfigGroup showGroup = config.group("Show"); foreach (const KSharedPtr& service, pluginServices) { if (!showGroup.readEntry(service->desktopEntryName(), true)) { // The plugin has been disabled continue; } // Old API (kdelibs-4.6.0 only) KFileItemActionPlugin* plugin = service->createInstance(); if (plugin) { plugin->setParent(m_popup); m_popup->addActions(plugin->actions(props, m_mainWindow)); } // New API (kdelibs >= 4.6.1) KAbstractFileItemActionPlugin* abstractPlugin = service->createInstance(); if (abstractPlugin) { abstractPlugin->setParent(m_popup); m_popup->addActions(abstractPlugin->actions(props, m_mainWindow)); } } } void DolphinContextMenu::addVersionControlPluginActions() { const DolphinView* view = m_mainWindow->activeViewContainer()->view(); const QList versionControlActions = view->versionControlActions(m_selectedItems); if (!versionControlActions.isEmpty()) { foreach (QAction* action, versionControlActions) { m_popup->addAction(action); } m_popup->addSeparator(); } } void DolphinContextMenu::addCustomActions() { foreach (QAction* action, m_customActions) { m_popup->addAction(action); } } void DolphinContextMenu::updateRemoveAction() { const KActionCollection* collection = m_mainWindow->actionCollection(); const bool moveToTrash = selectedItemsProperties().isLocal() && !m_shiftPressed; // Using m_removeAction->setText(action->text()) does not apply the &-shortcut. // This is only done until the original action has been shown at least once. To // bypass this issue, the text and &-shortcut is applied manually. const QAction* action = 0; if (moveToTrash) { action = collection->action("move_to_trash"); m_removeAction->setText(i18nc("@action:inmenu", "&Move to Trash")); } else { action = collection->action("delete"); m_removeAction->setText(i18nc("@action:inmenu", "&Delete")); } m_removeAction->setIcon(action->icon()); m_removeAction->setShortcuts(action->shortcuts()); } #include "dolphincontextmenu.moc" diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 0f075a11c..a379f31b0 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -1,2287 +1,2287 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * Copyright (C) 2006 by Stefan Monov * * Copyright (C) 2006 by Cvetoslav Ludmiloff * * * * 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 "dolphinmainwindow.h" #include #include "dolphinapplication.h" #include "dolphindockwidget.h" #include "dolphincontextmenu.h" #include "dolphinnewfilemenu.h" #include "dolphinviewcontainer.h" #ifdef HAVE_NEPOMUK #include "panels/search/searchpanel.h" #include #endif #include "panels/folders/folderspanel.h" #include "panels/places/placespanel.h" #include "panels/information/informationpanel.h" #include "search/dolphinsearchbox.h" #include "search/dolphinsearchinformation.h" #include "settings/dolphinsettings.h" #include "settings/dolphinsettingsdialog.h" #include "statusbar/dolphinstatusbar.h" #include "views/dolphinviewactionhandler.h" #include "views/dolphinremoteencoding.h" -#include "views/draganddrophelper.h" #include "views/viewproperties.h" #ifndef Q_OS_WIN #include "panels/terminal/terminalpanel.h" #endif #include "dolphin_generalsettings.h" #include "dolphin_iconsmodesettings.h" #include "dolphin_searchsettings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Menu shown when pressing the configure-button in the toolbar. */ class ToolBarMenu : public KMenu { public: ToolBarMenu(QWidget* parent); virtual ~ToolBarMenu(); protected: virtual void showEvent(QShowEvent* event); }; /* * Remembers the tab configuration if a tab has been closed. * Each closed tab can be restored by the menu * "Go -> Recently Closed Tabs". */ struct ClosedTab { KUrl primaryUrl; KUrl secondaryUrl; bool isSplit; }; Q_DECLARE_METATYPE(ClosedTab) DolphinMainWindow::DolphinMainWindow() : KXmlGuiWindow(0), m_newFileMenu(0), m_tabBar(0), m_activeViewContainer(0), m_centralWidgetLayout(0), m_tabIndex(0), m_viewTab(), m_actionHandler(0), m_remoteEncoding(0), m_settingsDialog(), m_toolBarSpacer(0), m_openToolBarMenuButton(0), m_updateToolBarTimer(0), m_lastHandleUrlStatJob(0), m_searchDockIsTemporaryVisible(false) { // Workaround for a X11-issue in combination with KModifierInfo // (see DolphinContextMenu::initializeModifierKeyInfo() for // more information): DolphinContextMenu::initializeModifierKeyInfo(); setObjectName("Dolphin#"); m_viewTab.append(ViewTab()); KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self(); undoManager->setUiInterface(new UndoUiInterface()); connect(undoManager, SIGNAL(undoAvailable(bool)), this, SLOT(slotUndoAvailable(bool))); connect(undoManager, SIGNAL(undoTextChanged(const QString&)), this, SLOT(slotUndoTextChanged(const QString&))); connect(undoManager, SIGNAL(jobRecordingStarted(CommandType)), this, SLOT(clearStatusBar())); connect(undoManager, SIGNAL(jobRecordingFinished(CommandType)), 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(); GeneralSettings* generalSettings = settings.generalSettings(); const bool firstRun = generalSettings->firstRun(); if (firstRun) { generalSettings->setViewPropsTimestamp(QDateTime::currentDateTime()); } setAcceptDrops(true); m_viewTab[m_tabIndex].splitter = new QSplitter(this); m_viewTab[m_tabIndex].splitter->setChildrenCollapsible(false); setupActions(); const KUrl homeUrl(generalSettings->homeUrl()); setUrlAsCaption(homeUrl); m_actionHandler = new DolphinViewActionHandler(actionCollection(), this); connect(m_actionHandler, SIGNAL(actionBeingHandled()), SLOT(clearStatusBar())); connect(m_actionHandler, SIGNAL(createDirectory()), SLOT(createDirectory())); m_viewTab[m_tabIndex].primaryView = createViewContainer(homeUrl, m_viewTab[m_tabIndex].splitter); m_activeViewContainer = m_viewTab[m_tabIndex].primaryView; connectViewSignals(m_activeViewContainer); DolphinView* view = m_activeViewContainer->view(); m_activeViewContainer->show(); m_actionHandler->setCurrentView(view); m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler); connect(this, SIGNAL(urlChanged(const KUrl&)), m_remoteEncoding, SLOT(slotAboutToOpenUrl())); m_tabBar = new KTabBar(this); m_tabBar->setMovable(true); m_tabBar->setTabsClosable(true); connect(m_tabBar, SIGNAL(currentChanged(int)), this, SLOT(setActiveTab(int))); connect(m_tabBar, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int))); connect(m_tabBar, SIGNAL(contextMenu(int, const QPoint&)), this, SLOT(openTabContextMenu(int, const QPoint&))); connect(m_tabBar, SIGNAL(newTabRequest()), this, SLOT(openNewTab())); connect(m_tabBar, SIGNAL(testCanDecode(const QDragMoveEvent*, bool&)), this, SLOT(slotTestCanDecode(const QDragMoveEvent*, bool&))); connect(m_tabBar, SIGNAL(mouseMiddleClick(int)), this, SLOT(closeTab(int))); connect(m_tabBar, SIGNAL(tabMoved(int, int)), this, SLOT(slotTabMoved(int, int))); connect(m_tabBar, SIGNAL(receivedDropEvent(int, QDropEvent*)), this, SLOT(tabDropEvent(int, QDropEvent*))); m_tabBar->blockSignals(true); // signals get unblocked after at least 2 tabs are open QWidget* centralWidget = new QWidget(this); m_centralWidgetLayout = new QVBoxLayout(centralWidget); m_centralWidgetLayout->setSpacing(0); m_centralWidgetLayout->setMargin(0); m_centralWidgetLayout->addWidget(m_tabBar); m_centralWidgetLayout->addWidget(m_viewTab[m_tabIndex].splitter, 1); setCentralWidget(centralWidget); setupDockWidgets(); emit urlChanged(homeUrl); setupGUI(Keys | Save | Create | ToolBar); stateChanged("new_file"); QClipboard* clipboard = QApplication::clipboard(); connect(clipboard, SIGNAL(dataChanged()), this, SLOT(updatePasteAction())); if (generalSettings->splitView()) { toggleSplitView(); } updateEditActions(); updateViewActions(); updateGoActions(); QAction* showFilterBarAction = actionCollection()->action("show_filter_bar"); showFilterBarAction->setChecked(generalSettings->filterBar()); if (firstRun) { menuBar()->setVisible(false); // Assure a proper default size if Dolphin runs the first time resize(750, 500); } const bool showMenu = !menuBar()->isHidden(); QAction* showMenuBarAction = actionCollection()->action(KStandardAction::name(KStandardAction::ShowMenubar)); showMenuBarAction->setChecked(showMenu); // workaround for bug #171080 if (!showMenu) { createToolBarMenuButton(); } } DolphinMainWindow::~DolphinMainWindow() { } void DolphinMainWindow::openDirectories(const QList& dirs) { if (dirs.isEmpty()) { return; } if (dirs.count() == 1) { m_activeViewContainer->setUrl(dirs.first()); return; } const int oldOpenTabsCount = m_viewTab.count(); const GeneralSettings* generalSettings = DolphinSettings::instance().generalSettings(); const bool hasSplitView = generalSettings->splitView(); // Open each directory inside a new tab. If the "split view" option has been enabled, // always show two directories within one tab. QList::const_iterator it = dirs.begin(); while (it != dirs.end()) { openNewTab(*it); ++it; if (hasSplitView && (it != dirs.end())) { const int tabIndex = m_viewTab.count() - 1; m_viewTab[tabIndex].secondaryView->setUrl(*it); ++it; } } // Remove the previously opened tabs for (int i = 0; i < oldOpenTabsCount; ++i) { closeTab(0); } } void DolphinMainWindow::openFiles(const QList& files) { if (files.isEmpty()) { return; } // Get all distinct directories from 'files' and open a tab // for each directory. If the "split view" option is enabled, two // directories are shown inside one tab (see openDirectories()). QList dirs; foreach (const KUrl& url, files) { const KUrl dir(url.directory()); if (!dirs.contains(dir)) { dirs.append(dir); } } openDirectories(dirs); // Select the files. Although the files can be split between several // tabs, there is no need to split 'files' accordingly, as // the DolphinView will just ignore invalid selections. const int tabCount = m_viewTab.count(); for (int i = 0; i < tabCount; ++i) { m_viewTab[i].primaryView->view()->markUrlsAsSelected(files); if (m_viewTab[i].secondaryView) { m_viewTab[i].secondaryView->view()->markUrlsAsSelected(files); } } } void DolphinMainWindow::showCommand(CommandType command) { DolphinStatusBar* statusBar = m_activeViewContainer->statusBar(); switch (command) { case KIO::FileUndoManager::Copy: statusBar->setMessage(i18nc("@info:status", "Successfully copied."), DolphinStatusBar::OperationCompleted); break; case KIO::FileUndoManager::Move: statusBar->setMessage(i18nc("@info:status", "Successfully moved."), DolphinStatusBar::OperationCompleted); break; case KIO::FileUndoManager::Link: statusBar->setMessage(i18nc("@info:status", "Successfully linked."), DolphinStatusBar::OperationCompleted); break; case KIO::FileUndoManager::Trash: statusBar->setMessage(i18nc("@info:status", "Successfully moved to trash."), DolphinStatusBar::OperationCompleted); break; case KIO::FileUndoManager::Rename: statusBar->setMessage(i18nc("@info:status", "Successfully renamed."), DolphinStatusBar::OperationCompleted); break; case KIO::FileUndoManager::Mkdir: statusBar->setMessage(i18nc("@info:status", "Created folder."), DolphinStatusBar::OperationCompleted); break; default: break; } } void DolphinMainWindow::pasteIntoFolder() { m_activeViewContainer->view()->pasteIntoFolder(); } void DolphinMainWindow::changeUrl(const KUrl& url) { if (!KProtocolManager::supportsListing(url)) { // The URL navigator only checks for validity, not // if the URL can be listed. An error message is // shown due to DolphinViewContainer::restoreView(). return; } DolphinViewContainer* view = activeViewContainer(); if (view) { view->setUrl(url); updateEditActions(); updateViewActions(); updateGoActions(); setUrlAsCaption(url); if (m_viewTab.count() > 1) { m_tabBar->setTabText(m_tabIndex, squeezedText(tabName(m_activeViewContainer->url()))); } const QString iconName = KMimeType::iconNameForUrl(url); m_tabBar->setTabIcon(m_tabIndex, KIcon(iconName)); emit urlChanged(url); } } void DolphinMainWindow::slotEditableStateChanged(bool editable) { KToggleAction* editableLocationAction = static_cast(actionCollection()->action("editable_location")); editableLocationAction->setChecked(editable); } void DolphinMainWindow::slotSelectionChanged(const KFileItemList& selection) { updateEditActions(); Q_ASSERT(m_viewTab[m_tabIndex].primaryView); int selectedUrlsCount = m_viewTab[m_tabIndex].primaryView->view()->selectedItemsCount(); if (m_viewTab[m_tabIndex].secondaryView) { selectedUrlsCount += m_viewTab[m_tabIndex].secondaryView->view()->selectedItemsCount(); } QAction* compareFilesAction = actionCollection()->action("compare_files"); if (selectedUrlsCount == 2) { compareFilesAction->setEnabled(isKompareInstalled()); } else { compareFilesAction->setEnabled(false); } emit selectionChanged(selection); } void DolphinMainWindow::slotRequestItemInfo(const KFileItem& item) { emit requestItemInfo(item); } void DolphinMainWindow::updateHistory() { const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); const int index = urlNavigator->historyIndex(); QAction* backAction = actionCollection()->action("go_back"); backAction->setToolTip(i18nc("@info", "Go back")); if (backAction) { backAction->setEnabled(index < urlNavigator->historySize() - 1); } QAction* forwardAction = actionCollection()->action("go_forward"); forwardAction->setToolTip(i18nc("@info", "Go forward")); if (forwardAction) { forwardAction->setEnabled(index > 0); } } void DolphinMainWindow::updateFilterBarAction(bool show) { QAction* showFilterBarAction = actionCollection()->action("show_filter_bar"); showFilterBarAction->setChecked(show); } void DolphinMainWindow::openNewMainWindow() { KRun::run("dolphin", KUrl::List(), this); } void DolphinMainWindow::openNewTab() { const bool isUrlEditable = m_activeViewContainer->urlNavigator()->isUrlEditable(); openNewTab(m_activeViewContainer->url()); m_tabBar->setCurrentIndex(m_viewTab.count() - 1); // The URL navigator of the new tab should have the same editable state // as the current tab KUrlNavigator* navigator = m_activeViewContainer->urlNavigator(); navigator->setUrlEditable(isUrlEditable); if (isUrlEditable) { // If a new tab is opened and the URL is editable, assure that // the user can edit the URL without manually setting the focus navigator->setFocus(); } } void DolphinMainWindow::openNewTab(const KUrl& url) { QWidget* focusWidget = QApplication::focusWidget(); if (m_viewTab.count() == 1) { // Only one view is open currently and hence no tab is shown at // all. Before creating a tab for 'url', provide a tab for the current URL. const KUrl currentUrl = m_activeViewContainer->url(); m_tabBar->addTab(KIcon(KMimeType::iconNameForUrl(currentUrl)), squeezedText(tabName(currentUrl))); m_tabBar->blockSignals(false); } m_tabBar->addTab(KIcon(KMimeType::iconNameForUrl(url)), squeezedText(tabName(url))); ViewTab viewTab; viewTab.splitter = new QSplitter(this); viewTab.splitter->setChildrenCollapsible(false); viewTab.primaryView = createViewContainer(url, viewTab.splitter); viewTab.primaryView->setActive(false); connectViewSignals(viewTab.primaryView); m_viewTab.append(viewTab); actionCollection()->action("close_tab")->setEnabled(true); // provide a split view, if the startup settings are set this way const GeneralSettings* generalSettings = DolphinSettings::instance().generalSettings(); if (generalSettings->splitView()) { const int tabIndex = m_viewTab.count() - 1; createSecondaryView(tabIndex); m_viewTab[tabIndex].secondaryView->setActive(true); m_viewTab[tabIndex].isPrimaryViewActive = false; } if (focusWidget) { // The DolphinViewContainer grabbed the keyboard focus. As the tab is opened // in background, assure that the previous focused widget gets the focus back. focusWidget->setFocus(); } } void DolphinMainWindow::activateNextTab() { if ((m_viewTab.count() == 1) || (m_tabBar->count() < 2)) { return; } const int tabIndex = (m_tabBar->currentIndex() + 1) % m_tabBar->count(); m_tabBar->setCurrentIndex(tabIndex); } void DolphinMainWindow::activatePrevTab() { if ((m_viewTab.count() == 1) || (m_tabBar->count() < 2)) { return; } int tabIndex = m_tabBar->currentIndex() - 1; if (tabIndex == -1) { tabIndex = m_tabBar->count() - 1; } m_tabBar->setCurrentIndex(tabIndex); } void DolphinMainWindow::openInNewTab() { const KFileItemList list = m_activeViewContainer->view()->selectedItems(); if (list.isEmpty()) { openNewTab(m_activeViewContainer->url()); } else if ((list.count() == 1) && list[0].isDir()) { openNewTab(list[0].url()); } } void DolphinMainWindow::openInNewWindow() { KUrl newWindowUrl; const KFileItemList list = m_activeViewContainer->view()->selectedItems(); if (list.isEmpty()) { newWindowUrl = m_activeViewContainer->url(); } else if ((list.count() == 1) && list[0].isDir()) { newWindowUrl = list[0].url(); } if (!newWindowUrl.isEmpty()) { KRun::run("dolphin", KUrl::List() << newWindowUrl, this); } } void DolphinMainWindow::toggleActiveView() { if (!m_viewTab[m_tabIndex].secondaryView) { // only one view is available return; } Q_ASSERT(m_activeViewContainer); Q_ASSERT(m_viewTab[m_tabIndex].primaryView); DolphinViewContainer* left = m_viewTab[m_tabIndex].primaryView; DolphinViewContainer* right = m_viewTab[m_tabIndex].secondaryView; setActiveViewContainer(m_activeViewContainer == right ? left : right); } void DolphinMainWindow::showEvent(QShowEvent* event) { KXmlGuiWindow::showEvent(event); if (!event->spontaneous()) { m_activeViewContainer->view()->setFocus(); } } void DolphinMainWindow::closeEvent(QCloseEvent* event) { DolphinSettings& settings = DolphinSettings::instance(); GeneralSettings* generalSettings = settings.generalSettings(); // Find out if Dolphin is closed directly by the user or // by the session manager because the session is closed bool closedByUser = true; DolphinApplication *application = qobject_cast(qApp); if (application && application->sessionSaving()) { closedByUser = false; } if ((m_viewTab.count() > 1) && generalSettings->confirmClosingMultipleTabs() && closedByUser) { // Ask the user if he really wants to quit and close all tabs. // Open a confirmation dialog with 3 buttons: // KDialog::Yes -> Quit // KDialog::No -> Close only the current tab // KDialog::Cancel -> do nothing KDialog *dialog = new KDialog(this, Qt::Dialog); dialog->setCaption(i18nc("@title:window", "Confirmation")); dialog->setButtons(KDialog::Yes | KDialog::No | KDialog::Cancel); dialog->setModal(true); dialog->setButtonGuiItem(KDialog::Yes, KStandardGuiItem::quit()); dialog->setButtonGuiItem(KDialog::No, KGuiItem(i18n("C&lose Current Tab"), KIcon("tab-close"))); dialog->setButtonGuiItem(KDialog::Cancel, KStandardGuiItem::cancel()); dialog->setDefaultButton(KDialog::Yes); bool doNotAskAgainCheckboxResult = false; const int result = KMessageBox::createKMessageBox(dialog, QMessageBox::Warning, i18n("You have multiple tabs open in this window, are you sure you want to quit?"), QStringList(), i18n("Do not ask again"), &doNotAskAgainCheckboxResult, KMessageBox::Notify); if (doNotAskAgainCheckboxResult) { generalSettings->setConfirmClosingMultipleTabs(false); } switch (result) { case KDialog::Yes: // Quit break; case KDialog::No: // Close only the current tab closeTab(); default: event->ignore(); return; } } generalSettings->setFirstRun(false); settings.save(); if (m_searchDockIsTemporaryVisible) { QDockWidget* searchDock = findChild("searchDock"); if (searchDock) { searchDock->hide(); } m_searchDockIsTemporaryVisible = false; } KXmlGuiWindow::closeEvent(event); } void DolphinMainWindow::saveProperties(KConfigGroup& group) { const int tabCount = m_viewTab.count(); group.writeEntry("Tab Count", tabCount); group.writeEntry("Active Tab Index", m_tabBar->currentIndex()); for (int i = 0; i < tabCount; ++i) { const DolphinViewContainer* cont = m_viewTab[i].primaryView; group.writeEntry(tabProperty("Primary URL", i), cont->url().url()); group.writeEntry(tabProperty("Primary Editable", i), cont->urlNavigator()->isUrlEditable()); cont = m_viewTab[i].secondaryView; if (cont) { group.writeEntry(tabProperty("Secondary URL", i), cont->url().url()); group.writeEntry(tabProperty("Secondary Editable", i), cont->urlNavigator()->isUrlEditable()); } } } void DolphinMainWindow::readProperties(const KConfigGroup& group) { const int tabCount = group.readEntry("Tab Count", 1); for (int i = 0; i < tabCount; ++i) { DolphinViewContainer* cont = m_viewTab[i].primaryView; cont->setUrl(group.readEntry(tabProperty("Primary URL", i))); const bool editable = group.readEntry(tabProperty("Primary Editable", i), false); cont->urlNavigator()->setUrlEditable(editable); cont = m_viewTab[i].secondaryView; const QString secondaryUrl = group.readEntry(tabProperty("Secondary URL", i)); if (!secondaryUrl.isEmpty()) { if (!cont) { // a secondary view should be shown, but no one is available // currently -> create a new view toggleSplitView(); cont = m_viewTab[i].secondaryView; Q_ASSERT(cont); } cont->setUrl(secondaryUrl); const bool editable = group.readEntry(tabProperty("Secondary Editable", i), false); cont->urlNavigator()->setUrlEditable(editable); } else if (cont) { // no secondary view should be shown, but the default setting shows // one already -> close the view toggleSplitView(); } // openNewTab() needs to be called only tabCount - 1 times if (i != tabCount - 1) { openNewTab(); } } const int index = group.readEntry("Active Tab Index", 0); m_tabBar->setCurrentIndex(index); } 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(); } void DolphinMainWindow::quit() { close(); } void DolphinMainWindow::showErrorMessage(const QString& message) { if (!message.isEmpty()) { DolphinStatusBar* statusBar = m_activeViewContainer->statusBar(); statusBar->setMessage(message, DolphinStatusBar::Error); } } void DolphinMainWindow::slotUndoAvailable(bool available) { QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo)); if (undoAction) { undoAction->setEnabled(available); } } void DolphinMainWindow::restoreClosedTab(QAction* action) { if (action->data().toBool()) { // clear all actions except the "Empty Recently Closed Tabs" // action and the separator QList actions = m_recentTabsMenu->menu()->actions(); const int count = actions.size(); for (int i = 2; i < count; ++i) { m_recentTabsMenu->menu()->removeAction(actions.at(i)); } } else { const ClosedTab closedTab = action->data().value(); openNewTab(closedTab.primaryUrl); m_tabBar->setCurrentIndex(m_viewTab.count() - 1); if (closedTab.isSplit) { // create secondary view toggleSplitView(); m_viewTab[m_tabIndex].secondaryView->setUrl(closedTab.secondaryUrl); } m_recentTabsMenu->removeAction(action); } if (m_recentTabsMenu->menu()->actions().count() == 2) { m_recentTabsMenu->setEnabled(false); } } void DolphinMainWindow::slotUndoTextChanged(const QString& text) { QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo)); if (undoAction) { undoAction->setText(text); } } void DolphinMainWindow::undo() { clearStatusBar(); KIO::FileUndoManager::self()->uiInterface()->setParentWidget(this); KIO::FileUndoManager::self()->undo(); } void DolphinMainWindow::cut() { m_activeViewContainer->view()->cutSelectedItems(); } void DolphinMainWindow::copy() { m_activeViewContainer->view()->copySelectedItems(); } void DolphinMainWindow::paste() { m_activeViewContainer->view()->paste(); } void DolphinMainWindow::find() { m_activeViewContainer->setSearchModeEnabled(true); } void DolphinMainWindow::slotSearchLocationChanged() { #ifdef HAVE_NEPOMUK QDockWidget* searchDock = findChild("searchDock"); if (!searchDock) { return; } SearchPanel* searchPanel = qobject_cast(searchDock->widget()); if (searchPanel) { searchPanel->setSearchLocation(SearchSettings::location() == QLatin1String("FromHere") ? SearchPanel::FromCurrentDir : SearchPanel::Everywhere); } #endif } void DolphinMainWindow::updatePasteAction() { QAction* pasteAction = actionCollection()->action(KStandardAction::name(KStandardAction::Paste)); QPair pasteInfo = m_activeViewContainer->view()->pasteInfo(); pasteAction->setEnabled(pasteInfo.first); pasteAction->setText(pasteInfo.second); } void DolphinMainWindow::selectAll() { clearStatusBar(); // if the URL navigator is editable and focused, select the whole // URL instead of all items of the view KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); QLineEdit* lineEdit = urlNavigator->editor()->lineEdit(); // krazy:exclude=qclasses const bool selectUrl = urlNavigator->isUrlEditable() && lineEdit->hasFocus(); if (selectUrl) { lineEdit->selectAll(); } else { m_activeViewContainer->view()->selectAll(); } } void DolphinMainWindow::invertSelection() { clearStatusBar(); m_activeViewContainer->view()->invertSelection(); } void DolphinMainWindow::toggleSplitView() { if (!m_viewTab[m_tabIndex].secondaryView) { createSecondaryView(m_tabIndex); setActiveViewContainer(m_viewTab[m_tabIndex].secondaryView); } else if (m_activeViewContainer == m_viewTab[m_tabIndex].secondaryView) { // remove secondary view m_viewTab[m_tabIndex].secondaryView->close(); m_viewTab[m_tabIndex].secondaryView->deleteLater(); m_viewTab[m_tabIndex].secondaryView = 0; setActiveViewContainer(m_viewTab[m_tabIndex].primaryView); } else { // The primary view is active and should be closed. Hence from a users point of view // the content of the secondary view should be moved to the primary view. // From an implementation point of view it is more efficient to close // the primary view and exchange the internal pointers afterwards. m_viewTab[m_tabIndex].primaryView->close(); m_viewTab[m_tabIndex].primaryView->deleteLater(); m_viewTab[m_tabIndex].primaryView = m_viewTab[m_tabIndex].secondaryView; m_viewTab[m_tabIndex].secondaryView = 0; setActiveViewContainer(m_viewTab[m_tabIndex].primaryView); } updateViewActions(); } void DolphinMainWindow::reloadView() { clearStatusBar(); m_activeViewContainer->view()->reload(); } void DolphinMainWindow::stopLoading() { m_activeViewContainer->view()->stopLoading(); } void DolphinMainWindow::enableStopAction() { actionCollection()->action("stop")->setEnabled(true); } void DolphinMainWindow::disableStopAction() { actionCollection()->action("stop")->setEnabled(false); } void DolphinMainWindow::showFilterBar() { m_activeViewContainer->setFilterBarVisible(true); } void DolphinMainWindow::toggleEditLocation() { clearStatusBar(); QAction* action = actionCollection()->action("editable_location"); KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); urlNavigator->setUrlEditable(action->isChecked()); } void DolphinMainWindow::replaceLocation() { KUrlNavigator* navigator = m_activeViewContainer->urlNavigator(); navigator->setUrlEditable(true); navigator->setFocus(); // select the whole text of the combo box editor QLineEdit* lineEdit = navigator->editor()->lineEdit(); // krazy:exclude=qclasses lineEdit->selectAll(); } void DolphinMainWindow::togglePanelLockState() { GeneralSettings* generalSettings = DolphinSettings::instance().generalSettings(); const bool newLockState = !generalSettings->lockPanels(); foreach (QObject* child, children()) { DolphinDockWidget* dock = qobject_cast(child); if (dock) { dock->setLocked(newLockState); } } generalSettings->setLockPanels(newLockState); } void DolphinMainWindow::slotPlacesPanelVisibilityChanged(bool visible) { const int tabCount = m_viewTab.count(); for (int i = 0; i < tabCount; ++i) { ViewTab& tab = m_viewTab[i]; Q_ASSERT(tab.primaryView); tab.primaryView->urlNavigator()->setPlacesSelectorVisible(!visible); if (tab.secondaryView) { tab.secondaryView->urlNavigator()->setPlacesSelectorVisible(!visible); } } } void DolphinMainWindow::goBack() { KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); urlNavigator->goBack(); if (urlNavigator->locationState().isEmpty()) { // An empty location state indicates a redirection URL, // which must be skipped too urlNavigator->goBack(); } } void DolphinMainWindow::goForward() { m_activeViewContainer->urlNavigator()->goForward(); } void DolphinMainWindow::goUp() { m_activeViewContainer->urlNavigator()->goUp(); } void DolphinMainWindow::goHome() { m_activeViewContainer->urlNavigator()->goHome(); } void DolphinMainWindow::goBack(Qt::MouseButtons buttons) { // The default case (left button pressed) is handled in goBack(). if (buttons == Qt::MidButton) { KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator(); const int index = urlNavigator->historyIndex() + 1; openNewTab(urlNavigator->locationUrl(index)); } } void DolphinMainWindow::goForward(Qt::MouseButtons buttons) { // The default case (left button pressed) is handled in goForward(). if (buttons == Qt::MidButton) { KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator(); const int index = urlNavigator->historyIndex() - 1; openNewTab(urlNavigator->locationUrl(index)); } } void DolphinMainWindow::goUp(Qt::MouseButtons buttons) { // The default case (left button pressed) is handled in goUp(). if (buttons == Qt::MidButton) { openNewTab(activeViewContainer()->url().upUrl()); } } void DolphinMainWindow::compareFiles() { // The method is only invoked if exactly 2 files have // been selected. The selected files may be: // - both in the primary view // - both in the secondary view // - one in the primary view and the other in the secondary // view Q_ASSERT(m_viewTab[m_tabIndex].primaryView); KUrl urlA; KUrl urlB; KFileItemList items = m_viewTab[m_tabIndex].primaryView->view()->selectedItems(); switch (items.count()) { case 0: { Q_ASSERT(m_viewTab[m_tabIndex].secondaryView); items = m_viewTab[m_tabIndex].secondaryView->view()->selectedItems(); Q_ASSERT(items.count() == 2); urlA = items[0].url(); urlB = items[1].url(); break; } case 1: { urlA = items[0].url(); Q_ASSERT(m_viewTab[m_tabIndex].secondaryView); items = m_viewTab[m_tabIndex].secondaryView->view()->selectedItems(); Q_ASSERT(items.count() == 1); urlB = items[0].url(); break; } case 2: { urlA = items[0].url(); urlB = items[1].url(); break; } default: { // may not happen: compareFiles may only get invoked if 2 // files are selected Q_ASSERT(false); } } QString command("kompare -c \""); command.append(urlA.pathOrUrl()); command.append("\" \""); command.append(urlB.pathOrUrl()); command.append('\"'); KRun::runCommand(command, "Kompare", "kompare", this); } void DolphinMainWindow::toggleShowMenuBar() { const bool visible = menuBar()->isVisible(); menuBar()->setVisible(!visible); if (visible) { createToolBarMenuButton(); } else { deleteToolBarMenuButton(); } } void DolphinMainWindow::openTerminal() { QString dir(QDir::homePath()); // If the given directory is not local, it can still be the URL of an // ioslave using UDS_LOCAL_PATH which to be converted first. KUrl url = KIO::NetAccess::mostLocalUrl(m_activeViewContainer->url(), this); //If the URL is local after the above conversion, set the directory. if (url.isLocalFile()) { dir = url.toLocalFile(); } KToolInvocation::invokeTerminal(QString(), dir); } void DolphinMainWindow::editSettings() { if (!m_settingsDialog) { const KUrl url = activeViewContainer()->url(); DolphinSettingsDialog* settingsDialog = new DolphinSettingsDialog(url, this); connect(settingsDialog, SIGNAL(settingsChanged()), this, SLOT(refreshViews())); settingsDialog->setAttribute(Qt::WA_DeleteOnClose); settingsDialog->show(); m_settingsDialog = settingsDialog; } else { m_settingsDialog.data()->raise(); } } void DolphinMainWindow::setActiveTab(int index) { Q_ASSERT(index >= 0); Q_ASSERT(index < m_viewTab.count()); if (index == m_tabIndex) { return; } // hide current tab content ViewTab& hiddenTab = m_viewTab[m_tabIndex]; hiddenTab.isPrimaryViewActive = hiddenTab.primaryView->isActive(); hiddenTab.primaryView->setActive(false); if (hiddenTab.secondaryView) { hiddenTab.secondaryView->setActive(false); } QSplitter* splitter = m_viewTab[m_tabIndex].splitter; splitter->hide(); m_centralWidgetLayout->removeWidget(splitter); // show active tab content m_tabIndex = index; ViewTab& viewTab = m_viewTab[index]; m_centralWidgetLayout->addWidget(viewTab.splitter, 1); viewTab.primaryView->show(); if (viewTab.secondaryView) { viewTab.secondaryView->show(); } viewTab.splitter->show(); setActiveViewContainer(viewTab.isPrimaryViewActive ? viewTab.primaryView : viewTab.secondaryView); } void DolphinMainWindow::closeTab() { closeTab(m_tabBar->currentIndex()); } void DolphinMainWindow::closeTab(int index) { Q_ASSERT(index >= 0); Q_ASSERT(index < m_viewTab.count()); if (m_viewTab.count() == 1) { // the last tab may never get closed return; } if (index == m_tabIndex) { // The tab that should be closed is the active tab. Activate the // previous tab before closing the tab. m_tabBar->setCurrentIndex((index > 0) ? index - 1 : 1); } rememberClosedTab(index); // delete tab m_viewTab[index].primaryView->deleteLater(); if (m_viewTab[index].secondaryView) { m_viewTab[index].secondaryView->deleteLater(); } m_viewTab[index].splitter->deleteLater(); m_viewTab.erase(m_viewTab.begin() + index); m_tabBar->blockSignals(true); m_tabBar->removeTab(index); if (m_tabIndex > index) { m_tabIndex--; Q_ASSERT(m_tabIndex >= 0); } // if only one tab is left, also remove the tab entry so that // closing the last tab is not possible if (m_viewTab.count() == 1) { m_tabBar->removeTab(0); actionCollection()->action("close_tab")->setEnabled(false); } else { m_tabBar->blockSignals(false); } } void DolphinMainWindow::openTabContextMenu(int index, const QPoint& pos) { KMenu menu(this); QAction* newTabAction = menu.addAction(KIcon("tab-new"), i18nc("@action:inmenu", "New Tab")); newTabAction->setShortcut(actionCollection()->action("new_tab")->shortcut()); QAction* detachTabAction = menu.addAction(KIcon("tab-detach"), i18nc("@action:inmenu", "Detach Tab")); QAction* closeOtherTabsAction = menu.addAction(KIcon("tab-close-other"), i18nc("@action:inmenu", "Close Other Tabs")); QAction* closeTabAction = menu.addAction(KIcon("tab-close"), i18nc("@action:inmenu", "Close Tab")); closeTabAction->setShortcut(actionCollection()->action("close_tab")->shortcut()); QAction* selectedAction = menu.exec(pos); if (selectedAction == newTabAction) { const ViewTab& tab = m_viewTab[index]; Q_ASSERT(tab.primaryView); const KUrl url = tab.secondaryView && tab.secondaryView->isActive() ? tab.secondaryView->url() : tab.primaryView->url(); openNewTab(url); m_tabBar->setCurrentIndex(m_viewTab.count() - 1); } else if (selectedAction == detachTabAction) { const QString separator(QLatin1Char(' ')); QString command = QLatin1String("dolphin"); const ViewTab& tab = m_viewTab[index]; Q_ASSERT(tab.primaryView); command += separator + tab.primaryView->url().url(); if (tab.secondaryView) { command += separator + tab.secondaryView->url().url(); command += separator + QLatin1String("-split"); } KRun::runCommand(command, this); closeTab(index); } else if (selectedAction == closeOtherTabsAction) { const int count = m_tabBar->count(); for (int i = 0; i < index; ++i) { closeTab(0); } for (int i = index + 1; i < count; ++i) { closeTab(1); } } else if (selectedAction == closeTabAction) { closeTab(index); } } void DolphinMainWindow::slotTabMoved(int from, int to) { m_viewTab.move(from, to); m_tabIndex = m_tabBar->currentIndex(); } void DolphinMainWindow::handlePlacesClick(const KUrl& url, Qt::MouseButtons buttons) { if (buttons & Qt::MidButton) { openNewTab(url); m_tabBar->setCurrentIndex(m_viewTab.count() - 1); } else { changeUrl(url); } } void DolphinMainWindow::slotTestCanDecode(const QDragMoveEvent* event, bool& canDecode) { canDecode = KUrl::List::canDecode(event->mimeData()); } void DolphinMainWindow::handleUrl(const KUrl& url) { delete m_lastHandleUrlStatJob; m_lastHandleUrlStatJob = 0; if (url.isLocalFile() && QFileInfo(url.toLocalFile()).isDir()) { activeViewContainer()->setUrl(url); } else if (KProtocolManager::supportsListing(url)) { // stat the URL to see if it is a dir or not m_lastHandleUrlStatJob = KIO::stat(url, KIO::HideProgressInfo); connect(m_lastHandleUrlStatJob, SIGNAL(result(KJob*)), this, SLOT(slotHandleUrlStatFinished(KJob*))); } else { new KRun(url, this); } } void DolphinMainWindow::slotHandleUrlStatFinished(KJob* job) { m_lastHandleUrlStatJob = 0; const KIO::UDSEntry entry = static_cast(job)->statResult(); const KUrl url = static_cast(job)->url(); if (entry.isDir()) { activeViewContainer()->setUrl(url); } else { new KRun(url, this); } } void DolphinMainWindow::tabDropEvent(int tab, QDropEvent* event) { const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData()); 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); } } void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable) { newFileMenu()->setEnabled(isFolderWritable); } void DolphinMainWindow::slotSearchModeChanged(bool enabled) { #ifdef HAVE_NEPOMUK const DolphinSearchInformation& searchInfo = DolphinSearchInformation::instance(); if (!searchInfo.isIndexingEnabled()) { return; } QDockWidget* searchDock = findChild("searchDock"); if (!searchDock) { return; } if (enabled) { if (!searchDock->isVisible()) { m_searchDockIsTemporaryVisible = true; } searchDock->show(); } else { if (searchDock->isVisible() && m_searchDockIsTemporaryVisible) { searchDock->hide(); } m_searchDockIsTemporaryVisible = false; } SearchPanel* searchPanel = qobject_cast(searchDock->widget()); if (!searchPanel) { return; } if (enabled) { SearchPanel::SearchLocation searchLocation = SearchPanel::Everywhere; const KUrl url = m_activeViewContainer->url(); const bool isSearchUrl = (url.protocol() == QLatin1String("nepomuksearch")); if ((SearchSettings::location() == QLatin1String("FromHere") && !isSearchUrl)) { searchLocation = SearchPanel::FromCurrentDir; } searchPanel->setSearchLocation(searchLocation); } else { searchPanel->setSearchLocation(SearchPanel::Everywhere); } #else Q_UNUSED(enabled); #endif } void DolphinMainWindow::openContextMenu(const KFileItem& item, const KUrl& url, const QList& customActions) { QPointer contextMenu = new DolphinContextMenu(this, item, url); contextMenu->setCustomActions(customActions); const DolphinContextMenu::Command command = contextMenu->open(); switch (command) { case DolphinContextMenu::OpenParentFolderInNewWindow: { KRun::run("dolphin", KUrl::List() << item.url().upUrl(), this); break; } case DolphinContextMenu::OpenParentFolderInNewTab: openNewTab(item.url().upUrl()); break; case DolphinContextMenu::None: default: break; } delete contextMenu; } void DolphinMainWindow::updateToolBarMenu() { KMenu* menu = qobject_cast(sender()); Q_ASSERT(menu); // All actions get cleared by KMenu::clear(). The sub-menus are deleted // by connecting to the aboutToHide() signal from the parent-menu. menu->clear(); const GeneralSettings* generalSettings = DolphinSettings::instance().generalSettings(); KActionCollection* ac = actionCollection(); // Add "Edit" actions bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) | addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Find)), menu) | addActionToMenu(ac->action("select_all"), menu) | addActionToMenu(ac->action("invert_selection"), menu); if (added) { menu->addSeparator(); } // Add "View" actions if (!generalSettings->showZoomSlider()) { addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomIn)), menu); addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomOut)), menu); menu->addSeparator(); } added = addActionToMenu(ac->action("view_mode"), menu) | addActionToMenu(ac->action("sort"), menu) | addActionToMenu(ac->action("additional_info"), menu) | addActionToMenu(ac->action("show_preview"), menu) | addActionToMenu(ac->action("show_in_groups"), menu) | addActionToMenu(ac->action("show_hidden_files"), menu); if (added) { menu->addSeparator(); } added = addActionToMenu(ac->action("split_view"), menu) | addActionToMenu(ac->action("reload"), menu) | addActionToMenu(ac->action("view_properties"), menu); if (added) { menu->addSeparator(); } addActionToMenu(ac->action("panels"), menu); KMenu* locationBarMenu = new KMenu(i18nc("@action:inmenu", "Location Bar"), menu); locationBarMenu->addAction(ac->action("editable_location")); locationBarMenu->addAction(ac->action("replace_location")); menu->addMenu(locationBarMenu); menu->addSeparator(); // Add "Go" menu KMenu* goMenu = new KMenu(i18nc("@action:inmenu", "Go"), menu); connect(menu, SIGNAL(aboutToHide()), goMenu, SLOT(deleteLater())); goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Back))); goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Forward))); goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Up))); goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Home))); goMenu->addAction(ac->action("closed_tabs")); menu->addMenu(goMenu); // Add "Tool" menu KMenu* toolsMenu = new KMenu(i18nc("@action:inmenu", "Tools"), menu); connect(menu, SIGNAL(aboutToHide()), toolsMenu, SLOT(deleteLater())); toolsMenu->addAction(ac->action("show_filter_bar")); toolsMenu->addAction(ac->action("compare_files")); toolsMenu->addAction(ac->action("open_terminal")); toolsMenu->addAction(ac->action("change_remote_encoding")); menu->addMenu(toolsMenu); // Add "Settings" menu entries addActionToMenu(ac->action(KStandardAction::name(KStandardAction::KeyBindings)), menu); addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ConfigureToolbars)), menu); addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Preferences)), menu); // Add "Help" menu KMenu* helpMenu = new KMenu(i18nc("@action:inmenu", "Help"), menu); connect(menu, SIGNAL(aboutToHide()), helpMenu, SLOT(deleteLater())); helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::HelpContents))); helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::WhatsThis))); helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::ReportBug))); helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::SwitchApplicationLanguage))); helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::AboutApp))); helpMenu->addAction(ac->action(KStandardAction::name(KStandardAction::AboutKDE))); menu->addMenu(helpMenu); menu->addSeparator(); addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)), menu); } void DolphinMainWindow::updateToolBar() { if (!menuBar()->isVisible()) { createToolBarMenuButton(); } } void DolphinMainWindow::slotToolBarSpacerDeleted() { m_toolBarSpacer = 0; m_updateToolBarTimer->start(); } void DolphinMainWindow::slotToolBarMenuButtonDeleted() { m_openToolBarMenuButton = 0; m_updateToolBarTimer->start(); } void DolphinMainWindow::slotToolBarIconSizeChanged(const QSize& iconSize) { if (m_openToolBarMenuButton) { m_openToolBarMenuButton->setIconSize(iconSize); } } void DolphinMainWindow::setActiveViewContainer(DolphinViewContainer* viewContainer) { Q_ASSERT(viewContainer); Q_ASSERT((viewContainer == m_viewTab[m_tabIndex].primaryView) || (viewContainer == m_viewTab[m_tabIndex].secondaryView)); if (m_activeViewContainer == viewContainer) { return; } m_activeViewContainer->setActive(false); m_activeViewContainer = viewContainer; // Activating the view container might trigger a recursive setActiveViewContainer() call // inside DolphinMainWindow::toggleActiveView() when having a split view. Temporary // disconnect the activated() signal in this case: disconnect(m_activeViewContainer->view(), SIGNAL(activated()), this, SLOT(toggleActiveView())); m_activeViewContainer->setActive(true); connect(m_activeViewContainer->view(), SIGNAL(activated()), this, SLOT(toggleActiveView())); m_actionHandler->setCurrentView(viewContainer->view()); updateHistory(); updateEditActions(); updateViewActions(); updateGoActions(); const KUrl url = m_activeViewContainer->url(); setUrlAsCaption(url); if (m_viewTab.count() > 1) { m_tabBar->setTabText(m_tabIndex, tabName(url)); m_tabBar->setTabIcon(m_tabIndex, KIcon(KMimeType::iconNameForUrl(url))); } emit urlChanged(url); } DolphinViewContainer* DolphinMainWindow::createViewContainer(const KUrl& url, QWidget* parent) { DolphinViewContainer* container = new DolphinViewContainer(url, parent); // The places-selector from the URL navigator should only be shown // if the places dock is invisible QDockWidget* placesDock = findChild("placesDock"); container->urlNavigator()->setPlacesSelectorVisible(!placesDock || !placesDock->isVisible()); return container; } void DolphinMainWindow::setupActions() { // setup 'File' menu m_newFileMenu = new DolphinNewFileMenu(this); KMenu* menu = m_newFileMenu->menu(); menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New")); menu->setIcon(KIcon("document-new")); connect(menu, SIGNAL(aboutToShow()), this, SLOT(updateNewMenu())); KAction* newWindow = actionCollection()->addAction("new_window"); newWindow->setIcon(KIcon("window-new")); newWindow->setText(i18nc("@action:inmenu File", "New &Window")); newWindow->setShortcut(Qt::CTRL | Qt::Key_N); connect(newWindow, SIGNAL(triggered()), this, SLOT(openNewMainWindow())); KAction* newTab = actionCollection()->addAction("new_tab"); newTab->setIcon(KIcon("tab-new")); newTab->setText(i18nc("@action:inmenu File", "New Tab")); newTab->setShortcut(KShortcut(Qt::CTRL | Qt::Key_T, Qt::CTRL | Qt::SHIFT | Qt::Key_N)); connect(newTab, SIGNAL(triggered()), this, SLOT(openNewTab())); KAction* closeTab = actionCollection()->addAction("close_tab"); closeTab->setIcon(KIcon("tab-close")); closeTab->setText(i18nc("@action:inmenu File", "Close Tab")); closeTab->setShortcut(Qt::CTRL | Qt::Key_W); closeTab->setEnabled(false); connect(closeTab, SIGNAL(triggered()), this, SLOT(closeTab())); KStandardAction::quit(this, SLOT(quit()), actionCollection()); // setup 'Edit' menu KStandardAction::undo(this, SLOT(undo()), actionCollection()); // need to remove shift+del from cut action, else the shortcut for deletejob // doesn't work KAction* cut = KStandardAction::cut(this, SLOT(cut()), actionCollection()); KShortcut cutShortcut = cut->shortcut(); cutShortcut.remove(Qt::SHIFT | Qt::Key_Delete, KShortcut::KeepEmpty); cut->setShortcut(cutShortcut); KStandardAction::copy(this, SLOT(copy()), actionCollection()); KAction* paste = KStandardAction::paste(this, SLOT(paste()), actionCollection()); // The text of the paste-action is modified dynamically by Dolphin // (e. g. to "Paste One Folder"). To prevent that the size of the toolbar changes // due to the long text, the text "Paste" is used: paste->setIconText(i18nc("@action:inmenu Edit", "Paste")); KStandardAction::find(this, SLOT(find()), actionCollection()); KAction* selectAll = actionCollection()->addAction("select_all"); selectAll->setText(i18nc("@action:inmenu Edit", "Select All")); selectAll->setShortcut(Qt::CTRL | Qt::Key_A); connect(selectAll, SIGNAL(triggered()), this, SLOT(selectAll())); KAction* invertSelection = actionCollection()->addAction("invert_selection"); invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection")); invertSelection->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_A); connect(invertSelection, SIGNAL(triggered()), this, SLOT(invertSelection())); // setup 'View' menu // (note that most of it is set up in DolphinViewActionHandler) KAction* split = actionCollection()->addAction("split_view"); split->setShortcut(Qt::Key_F3); updateSplitAction(); connect(split, SIGNAL(triggered()), this, SLOT(toggleSplitView())); KAction* reload = actionCollection()->addAction("reload"); reload->setText(i18nc("@action:inmenu View", "Reload")); reload->setShortcut(Qt::Key_F5); reload->setIcon(KIcon("view-refresh")); connect(reload, SIGNAL(triggered()), this, SLOT(reloadView())); KAction* stop = actionCollection()->addAction("stop"); stop->setText(i18nc("@action:inmenu View", "Stop")); stop->setToolTip(i18nc("@info", "Stop loading")); stop->setIcon(KIcon("process-stop")); connect(stop, SIGNAL(triggered()), this, SLOT(stopLoading())); KToggleAction* editableLocation = actionCollection()->add("editable_location"); editableLocation->setText(i18nc("@action:inmenu Navigation Bar", "Editable Location")); editableLocation->setShortcut(Qt::CTRL | Qt::Key_L); connect(editableLocation, SIGNAL(triggered()), this, SLOT(toggleEditLocation())); KAction* replaceLocation = actionCollection()->addAction("replace_location"); replaceLocation->setText(i18nc("@action:inmenu Navigation Bar", "Replace Location")); replaceLocation->setShortcut(Qt::Key_F6); connect(replaceLocation, SIGNAL(triggered()), this, SLOT(replaceLocation())); // setup 'Go' menu KAction* backAction = KStandardAction::back(this, SLOT(goBack()), actionCollection()); connect(backAction, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), this, SLOT(goBack(Qt::MouseButtons))); KShortcut backShortcut = backAction->shortcut(); backShortcut.setAlternate(Qt::Key_Backspace); backAction->setShortcut(backShortcut); m_recentTabsMenu = new KActionMenu(i18n("Recently Closed Tabs"), this); m_recentTabsMenu->setIcon(KIcon("edit-undo")); actionCollection()->addAction("closed_tabs", m_recentTabsMenu); connect(m_recentTabsMenu->menu(), SIGNAL(triggered(QAction *)), this, SLOT(restoreClosedTab(QAction *))); QAction* action = new QAction(i18n("Empty Recently Closed Tabs"), m_recentTabsMenu); action->setIcon(KIcon("edit-clear-list")); action->setData(QVariant::fromValue(true)); m_recentTabsMenu->addAction(action); m_recentTabsMenu->addSeparator(); m_recentTabsMenu->setEnabled(false); KAction* forwardAction = KStandardAction::forward(this, SLOT(goForward()), actionCollection()); connect(forwardAction, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), this, SLOT(goForward(Qt::MouseButtons))); KAction* upAction = KStandardAction::up(this, SLOT(goUp()), actionCollection()); connect(upAction, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), this, SLOT(goUp(Qt::MouseButtons))); KStandardAction::home(this, SLOT(goHome()), actionCollection()); // setup 'Tools' menu KAction* showFilterBar = actionCollection()->addAction("show_filter_bar"); showFilterBar->setText(i18nc("@action:inmenu Tools", "Show Filter Bar")); showFilterBar->setIcon(KIcon("view-filter")); showFilterBar->setShortcut(Qt::CTRL | Qt::Key_I); connect(showFilterBar, SIGNAL(triggered()), this, SLOT(showFilterBar())); KAction* compareFiles = actionCollection()->addAction("compare_files"); compareFiles->setText(i18nc("@action:inmenu Tools", "Compare Files")); compareFiles->setIcon(KIcon("kompare")); compareFiles->setEnabled(false); connect(compareFiles, SIGNAL(triggered()), this, SLOT(compareFiles())); KAction* openTerminal = actionCollection()->addAction("open_terminal"); openTerminal->setText(i18nc("@action:inmenu Tools", "Open Terminal")); openTerminal->setIcon(KIcon("utilities-terminal")); openTerminal->setShortcut(Qt::SHIFT | Qt::Key_F4); connect(openTerminal, SIGNAL(triggered()), this, SLOT(openTerminal())); // setup 'Settings' menu KStandardAction::showMenubar(this, SLOT(toggleShowMenuBar()), actionCollection()); KStandardAction::preferences(this, SLOT(editSettings()), actionCollection()); // not in menu actions QList nextTabKeys; nextTabKeys.append(KStandardShortcut::tabNext().primary()); nextTabKeys.append(QKeySequence(Qt::CTRL | Qt::Key_Tab)); QList prevTabKeys; prevTabKeys.append(KStandardShortcut::tabPrev().primary()); prevTabKeys.append(QKeySequence(Qt::CTRL | Qt::SHIFT | Qt::Key_Tab)); KAction* activateNextTab = actionCollection()->addAction("activate_next_tab"); activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab")); connect(activateNextTab, SIGNAL(triggered()), SLOT(activateNextTab())); activateNextTab->setShortcuts(QApplication::isRightToLeft() ? prevTabKeys : nextTabKeys); KAction* activatePrevTab = actionCollection()->addAction("activate_prev_tab"); activatePrevTab->setText(i18nc("@action:inmenu", "Activate Previous Tab")); connect(activatePrevTab, SIGNAL(triggered()), SLOT(activatePrevTab())); activatePrevTab->setShortcuts(QApplication::isRightToLeft() ? nextTabKeys : prevTabKeys); // for context menu KAction* openInNewTab = actionCollection()->addAction("open_in_new_tab"); openInNewTab->setText(i18nc("@action:inmenu", "Open in New Tab")); openInNewTab->setIcon(KIcon("tab-new")); connect(openInNewTab, SIGNAL(triggered()), this, SLOT(openInNewTab())); KAction* openInNewWindow = actionCollection()->addAction("open_in_new_window"); openInNewWindow->setText(i18nc("@action:inmenu", "Open in New Window")); openInNewWindow->setIcon(KIcon("window-new")); connect(openInNewWindow, SIGNAL(triggered()), this, SLOT(openInNewWindow())); } void DolphinMainWindow::setupDockWidgets() { const bool lock = DolphinSettings::instance().generalSettings()->lockPanels(); KDualAction* lockLayoutAction = actionCollection()->add("lock_panels"); lockLayoutAction->setActiveText(i18nc("@action:inmenu Panels", "Unlock Panels")); lockLayoutAction->setActiveIcon(KIcon("object-unlocked")); lockLayoutAction->setInactiveText(i18nc("@action:inmenu Panels", "Lock Panels")); lockLayoutAction->setInactiveIcon(KIcon("object-locked")); lockLayoutAction->setActive(lock); connect(lockLayoutAction, SIGNAL(triggered()), this, SLOT(togglePanelLockState())); // Setup "Information" DolphinDockWidget* infoDock = new DolphinDockWidget(i18nc("@title:window", "Information")); infoDock->setLocked(lock); infoDock->setObjectName("infoDock"); infoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); Panel* infoPanel = new InformationPanel(infoDock); infoPanel->setCustomContextMenuActions(QList() << lockLayoutAction); connect(infoPanel, SIGNAL(urlActivated(KUrl)), this, SLOT(handleUrl(KUrl))); infoDock->setWidget(infoPanel); QAction* infoAction = infoDock->toggleViewAction(); infoAction->setIcon(KIcon("dialog-information")); infoAction->setShortcut(Qt::Key_F11); addActionCloneToCollection(infoAction, "show_information_panel"); addDockWidget(Qt::RightDockWidgetArea, infoDock); connect(this, SIGNAL(urlChanged(KUrl)), infoPanel, SLOT(setUrl(KUrl))); connect(this, SIGNAL(selectionChanged(KFileItemList)), infoPanel, SLOT(setSelection(KFileItemList))); connect(this, SIGNAL(requestItemInfo(KFileItem)), infoPanel, SLOT(requestDelayedItemInfo(KFileItem))); // Setup "Folders" DolphinDockWidget* foldersDock = new DolphinDockWidget(i18nc("@title:window", "Folders")); foldersDock->setLocked(lock); foldersDock->setObjectName("foldersDock"); foldersDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); FoldersPanel* foldersPanel = new FoldersPanel(foldersDock); foldersPanel->setCustomContextMenuActions(QList() << lockLayoutAction); foldersDock->setWidget(foldersPanel); QAction* foldersAction = foldersDock->toggleViewAction(); foldersAction->setShortcut(Qt::Key_F7); foldersAction->setIcon(KIcon("folder")); addActionCloneToCollection(foldersAction, "show_folders_panel"); addDockWidget(Qt::LeftDockWidgetArea, foldersDock); connect(this, SIGNAL(urlChanged(KUrl)), foldersPanel, SLOT(setUrl(KUrl))); connect(foldersPanel, SIGNAL(changeUrl(KUrl, Qt::MouseButtons)), this, SLOT(handlePlacesClick(KUrl, Qt::MouseButtons))); // Setup "Terminal" #ifndef Q_OS_WIN DolphinDockWidget* terminalDock = new DolphinDockWidget(i18nc("@title:window Shell terminal", "Terminal")); terminalDock->setLocked(lock); terminalDock->setObjectName("terminalDock"); terminalDock->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea); Panel* terminalPanel = new TerminalPanel(terminalDock); terminalPanel->setCustomContextMenuActions(QList() << lockLayoutAction); terminalDock->setWidget(terminalPanel); connect(terminalPanel, SIGNAL(hideTerminalPanel()), terminalDock, SLOT(hide())); connect(terminalDock, SIGNAL(visibilityChanged(bool)), terminalPanel, SLOT(dockVisibilityChanged())); QAction* terminalAction = terminalDock->toggleViewAction(); terminalAction->setShortcut(Qt::Key_F4); terminalAction->setIcon(KIcon("utilities-terminal")); addActionCloneToCollection(terminalAction, "show_terminal_panel"); addDockWidget(Qt::BottomDockWidgetArea, terminalDock); connect(this, SIGNAL(urlChanged(KUrl)), terminalPanel, SLOT(setUrl(KUrl))); #endif // Setup "Search" #ifdef HAVE_NEPOMUK DolphinDockWidget* searchDock = new DolphinDockWidget(i18nc("@title:window", "Search")); searchDock->setLocked(lock); searchDock->setObjectName("searchDock"); searchDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); Panel* searchPanel = new SearchPanel(searchDock); searchPanel->setCustomContextMenuActions(QList() << lockLayoutAction); connect(searchPanel, SIGNAL(urlActivated(KUrl)), this, SLOT(handleUrl(KUrl))); searchDock->setWidget(searchPanel); QAction* searchAction = searchDock->toggleViewAction(); searchAction->setShortcut(Qt::Key_F12); searchAction->setIcon(KIcon("system-search")); addActionCloneToCollection(searchAction, "show_search_panel"); addDockWidget(Qt::RightDockWidgetArea, searchDock); connect(this, SIGNAL(urlChanged(KUrl)), searchPanel, SLOT(setUrl(KUrl))); #endif const bool firstRun = DolphinSettings::instance().generalSettings()->firstRun(); if (firstRun) { infoDock->hide(); foldersDock->hide(); #ifndef Q_OS_WIN terminalDock->hide(); #endif #ifdef HAVE_NEPOMUK searchDock->hide(); #endif } // Setup "Places" DolphinDockWidget* placesDock = new DolphinDockWidget(i18nc("@title:window", "Places")); placesDock->setLocked(lock); placesDock->setObjectName("placesDock"); placesDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); PlacesPanel* placesPanel = new PlacesPanel(placesDock); QAction* separator = new QAction(placesPanel); separator->setSeparator(true); QList placesActions; placesActions.append(separator); placesActions.append(lockLayoutAction); placesPanel->addActions(placesActions); placesPanel->setModel(DolphinSettings::instance().placesModel()); placesPanel->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); placesDock->setWidget(placesPanel); QAction* placesAction = placesDock->toggleViewAction(); placesAction->setShortcut(Qt::Key_F9); placesAction->setIcon(KIcon("bookmarks")); addActionCloneToCollection(placesAction, "show_places_panel"); addDockWidget(Qt::LeftDockWidgetArea, placesDock); connect(placesPanel, SIGNAL(urlChanged(KUrl, Qt::MouseButtons)), this, SLOT(handlePlacesClick(KUrl, Qt::MouseButtons))); connect(this, SIGNAL(urlChanged(KUrl)), placesPanel, SLOT(setUrl(KUrl))); connect(placesDock, SIGNAL(visibilityChanged(bool)), this, SLOT(slotPlacesPanelVisibilityChanged(bool))); // Add actions into the "Panels" menu KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Panels"), this); actionCollection()->addAction("panels", panelsMenu); panelsMenu->setDelayed(false); panelsMenu->addAction(placesAction); panelsMenu->addAction(infoAction); panelsMenu->addAction(foldersAction); #ifndef Q_OS_WIN panelsMenu->addAction(terminalAction); #endif #ifdef HAVE_NEPOMUK panelsMenu->addAction(searchAction); #endif panelsMenu->addSeparator(); panelsMenu->addAction(lockLayoutAction); } void DolphinMainWindow::updateEditActions() { const KFileItemList list = m_activeViewContainer->view()->selectedItems(); if (list.isEmpty()) { stateChanged("has_no_selection"); } else { stateChanged("has_selection"); KActionCollection* col = actionCollection(); QAction* renameAction = col->action("rename"); QAction* moveToTrashAction = col->action("move_to_trash"); QAction* deleteAction = col->action("delete"); QAction* cutAction = col->action(KStandardAction::name(KStandardAction::Cut)); QAction* deleteWithTrashShortcut = col->action("delete_shortcut"); // see DolphinViewActionHandler KFileItemListProperties capabilities(list); const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving(); renameAction->setEnabled(capabilities.supportsMoving()); moveToTrashAction->setEnabled(enableMoveToTrash); deleteAction->setEnabled(capabilities.supportsDeleting()); deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash); cutAction->setEnabled(capabilities.supportsMoving()); } updatePasteAction(); } void DolphinMainWindow::updateViewActions() { m_actionHandler->updateViewActions(); QAction* showFilterBarAction = actionCollection()->action("show_filter_bar"); showFilterBarAction->setChecked(m_activeViewContainer->isFilterBarVisible()); updateSplitAction(); QAction* editableLocactionAction = actionCollection()->action("editable_location"); const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); editableLocactionAction->setChecked(urlNavigator->isUrlEditable()); } void DolphinMainWindow::updateGoActions() { QAction* goUpAction = actionCollection()->action(KStandardAction::name(KStandardAction::Up)); const KUrl currentUrl = m_activeViewContainer->url(); goUpAction->setEnabled(currentUrl.upUrl() != currentUrl); } void DolphinMainWindow::createToolBarMenuButton() { if (m_toolBarSpacer && m_openToolBarMenuButton) { return; } Q_ASSERT(!m_toolBarSpacer); Q_ASSERT(!m_openToolBarMenuButton); m_toolBarSpacer = new QWidget(this); m_toolBarSpacer->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); m_openToolBarMenuButton = new QToolButton(this); m_openToolBarMenuButton->setIcon(KIcon("configure")); m_openToolBarMenuButton->setPopupMode(QToolButton::InstantPopup); m_openToolBarMenuButton->setToolTip(i18nc("@info:tooltip", "Configure and control Dolphin")); KMenu* toolBarMenu = new ToolBarMenu(m_openToolBarMenuButton); connect(toolBarMenu, SIGNAL(aboutToShow()), this, SLOT(updateToolBarMenu())); m_openToolBarMenuButton->setMenu(toolBarMenu); toolBar()->addWidget(m_toolBarSpacer); toolBar()->addWidget(m_openToolBarMenuButton); connect(toolBar(), SIGNAL(iconSizeChanged(QSize)), this, SLOT(slotToolBarIconSizeChanged(QSize))); // The added widgets are owned by the toolbar and may get deleted when e.g. the toolbar // gets edited. In this case we must add them again. The adding is done asynchronously by // m_updateToolBarTimer. connect(m_toolBarSpacer, SIGNAL(destroyed()), this, SLOT(slotToolBarSpacerDeleted())); connect(m_openToolBarMenuButton, SIGNAL(destroyed()), this, SLOT(slotToolBarMenuButtonDeleted())); m_updateToolBarTimer = new QTimer(this); m_updateToolBarTimer->setInterval(500); connect(m_updateToolBarTimer, SIGNAL(timeout()), this, SLOT(updateToolBar())); } void DolphinMainWindow::deleteToolBarMenuButton() { delete m_toolBarSpacer; m_toolBarSpacer = 0; delete m_openToolBarMenuButton; m_openToolBarMenuButton = 0; delete m_updateToolBarTimer; m_updateToolBarTimer = 0; } bool DolphinMainWindow::addActionToMenu(QAction* action, KMenu* menu) { Q_ASSERT(action); Q_ASSERT(menu); const KToolBar* toolBarWidget = toolBar(); foreach (const QWidget* widget, action->associatedWidgets()) { if (widget == toolBarWidget) { return false; } } menu->addAction(action); return true; } void DolphinMainWindow::rememberClosedTab(int index) { KMenu* tabsMenu = m_recentTabsMenu->menu(); const QString primaryPath = m_viewTab[index].primaryView->url().path(); const QString iconName = KMimeType::iconNameForUrl(primaryPath); QAction* action = new QAction(squeezedText(primaryPath), tabsMenu); ClosedTab closedTab; closedTab.primaryUrl = m_viewTab[index].primaryView->url(); if (m_viewTab[index].secondaryView) { closedTab.secondaryUrl = m_viewTab[index].secondaryView->url(); closedTab.isSplit = true; } else { closedTab.isSplit = false; } action->setData(QVariant::fromValue(closedTab)); action->setIcon(KIcon(iconName)); // add the closed tab menu entry after the separator and // "Empty Recently Closed Tabs" entry if (tabsMenu->actions().size() == 2) { tabsMenu->addAction(action); } else { tabsMenu->insertAction(tabsMenu->actions().at(2), action); } // assure that only up to 8 closed tabs are shown in the menu if (tabsMenu->actions().size() > 8) { tabsMenu->removeAction(tabsMenu->actions().last()); } actionCollection()->action("closed_tabs")->setEnabled(true); KAcceleratorManager::manage(tabsMenu); } void DolphinMainWindow::refreshViews() { Q_ASSERT(m_viewTab[m_tabIndex].primaryView); // remember the current active view, as because of // the refreshing the active view might change to // the secondary view DolphinViewContainer* activeViewContainer = m_activeViewContainer; const int tabCount = m_viewTab.count(); for (int i = 0; i < tabCount; ++i) { m_viewTab[i].primaryView->refresh(); if (m_viewTab[i].secondaryView) { m_viewTab[i].secondaryView->refresh(); } } setActiveViewContainer(activeViewContainer); const GeneralSettings* generalSettings = DolphinSettings::instance().generalSettings(); if (generalSettings->modifiedStartupSettings()) { // The startup settings have been changed by the user (see bug #254947). // Synchronize the split-view setting with the active view: const bool splitView = generalSettings->splitView(); const ViewTab& activeTab = m_viewTab[m_tabIndex]; const bool toggle = ( splitView && !activeTab.secondaryView) || (!splitView && activeTab.secondaryView); if (toggle) { toggleSplitView(); } } } void DolphinMainWindow::clearStatusBar() { m_activeViewContainer->statusBar()->clear(); } void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container) { connect(container, SIGNAL(showFilterBarChanged(bool)), this, SLOT(updateFilterBarAction(bool))); connect(container, SIGNAL(writeStateChanged(bool)), this, SLOT(slotWriteStateChanged(bool))); connect(container, SIGNAL(searchModeChanged(bool)), this, SLOT(slotSearchModeChanged(bool))); const DolphinSearchBox* searchBox = container->searchBox(); connect(searchBox, SIGNAL(searchLocationChanged(SearchLocation)), this, SLOT(slotSearchLocationChanged())); DolphinView* view = container->view(); connect(view, SIGNAL(selectionChanged(KFileItemList)), this, SLOT(slotSelectionChanged(KFileItemList))); connect(view, SIGNAL(requestItemInfo(KFileItem)), this, SLOT(slotRequestItemInfo(KFileItem))); connect(view, SIGNAL(activated()), this, SLOT(toggleActiveView())); connect(view, SIGNAL(tabRequested(const KUrl&)), this, SLOT(openNewTab(const KUrl&))); connect(view, SIGNAL(requestContextMenu(KFileItem, const KUrl&, const QList&)), this, SLOT(openContextMenu(KFileItem, const KUrl&, const QList&))); connect(view, SIGNAL(startedPathLoading(KUrl)), this, SLOT(enableStopAction())); connect(view, SIGNAL(finishedPathLoading(KUrl)), this, SLOT(disableStopAction())); const KUrlNavigator* navigator = container->urlNavigator(); connect(navigator, SIGNAL(urlChanged(const KUrl&)), this, SLOT(changeUrl(const KUrl&))); connect(navigator, SIGNAL(historyChanged()), this, SLOT(updateHistory())); connect(navigator, SIGNAL(editableStateChanged(bool)), this, SLOT(slotEditableStateChanged(bool))); connect(navigator, SIGNAL(tabRequested(const KUrl&)), this, SLOT(openNewTab(KUrl))); } void DolphinMainWindow::updateSplitAction() { QAction* splitAction = actionCollection()->action("split_view"); if (m_viewTab[m_tabIndex].secondaryView) { if (m_activeViewContainer == m_viewTab[m_tabIndex].secondaryView) { splitAction->setText(i18nc("@action:intoolbar Close right view", "Close")); splitAction->setToolTip(i18nc("@info", "Close right view")); splitAction->setIcon(KIcon("view-right-close")); } else { splitAction->setText(i18nc("@action:intoolbar Close left view", "Close")); splitAction->setToolTip(i18nc("@info", "Close left view")); splitAction->setIcon(KIcon("view-left-close")); } } else { splitAction->setText(i18nc("@action:intoolbar Split view", "Split")); splitAction->setToolTip(i18nc("@info", "Split view")); splitAction->setIcon(KIcon("view-right-new")); } } QString DolphinMainWindow::tabName(const KUrl& url) const { QString name; if (url.equals(KUrl("file:///"))) { name = '/'; } else { name = url.fileName(); if (name.isEmpty()) { name = url.protocol(); } else { // Make sure that a '&' inside the directory name is displayed correctly // and not misinterpreted as a keyboard shortcut in QTabBar::setTabText() name.replace('&', "&&"); } } return name; } bool DolphinMainWindow::isKompareInstalled() const { static bool initialized = false; static bool installed = false; if (!initialized) { // TODO: maybe replace this approach later by using a menu // plugin like kdiff3plugin.cpp installed = !KGlobal::dirs()->findExe("kompare").isEmpty(); initialized = true; } return installed; } void DolphinMainWindow::createSecondaryView(int tabIndex) { QSplitter* splitter = m_viewTab[tabIndex].splitter; 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() << newWidth << newWidth); connectViewSignals(m_viewTab[tabIndex].secondaryView); m_viewTab[tabIndex].secondaryView->setActive(false); m_viewTab[tabIndex].secondaryView->show(); } QString DolphinMainWindow::tabProperty(const QString& property, int tabIndex) const { return "Tab " + QString::number(tabIndex) + ' ' + property; } void DolphinMainWindow::setUrlAsCaption(const KUrl& url) { QString caption; if (!url.isLocalFile()) { caption.append(url.protocol() + " - "); if (url.hasHost()) { caption.append(url.host() + " - "); } } const QString fileName = url.fileName().isEmpty() ? "/" : url.fileName(); caption.append(fileName); setCaption(caption); } QString DolphinMainWindow::squeezedText(const QString& text) const { const QFontMetrics fm = fontMetrics(); return fm.elidedText(text, Qt::ElideMiddle, fm.maxWidth() * 10); } void DolphinMainWindow::addActionCloneToCollection(QAction* action, const QString& actionName) { KAction* actionClone = actionCollection()->addAction(actionName); actionClone->setText(action->text()); actionClone->setIcon(action->icon()); connect(actionClone, SIGNAL(triggered()), action, SLOT(trigger())); } DolphinMainWindow::UndoUiInterface::UndoUiInterface() : KIO::FileUndoManager::UiInterface() { } DolphinMainWindow::UndoUiInterface::~UndoUiInterface() { } void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job* job) { DolphinMainWindow* mainWin= qobject_cast(parentWidget()); if (mainWin) { DolphinStatusBar* statusBar = mainWin->activeViewContainer()->statusBar(); statusBar->setMessage(job->errorString(), DolphinStatusBar::Error); } else { KIO::FileUndoManager::UiInterface::jobError(job); } } ToolBarMenu::ToolBarMenu(QWidget* parent) : KMenu(parent) { } ToolBarMenu::~ToolBarMenu() { } void ToolBarMenu::showEvent(QShowEvent* event) { KMenu::showEvent(event); // Adjust the position of the menu to be shown within the // Dolphin window to reduce the cases that sub-menus might overlap // the right screen border. QPoint pos; QWidget* button = parentWidget(); if (layoutDirection() == Qt::RightToLeft) { pos = button->mapToGlobal(QPoint(0, button->height())); } else { pos = button->mapToGlobal(QPoint(button->width(), button->height())); pos.rx() -= width(); } // Assure that the menu is not shown outside the screen boundaries and // that it does not overlap with the parent button. const QRect screen = QApplication::desktop()->screenGeometry(QCursor::pos()); if (pos.x() < screen.x()) { pos.rx() = screen.x(); } else if (pos.x() + width() > screen.x() + screen.width()) { pos.rx() = screen.x() + screen.width() - width(); } if (pos.y() < screen.y()) { pos.ry() = screen.y(); } else if (pos.y() + height() > screen.y() + screen.height()) { pos.ry() = button->mapToGlobal(QPoint(0, 0)).y() - height(); } move(pos); } #include "dolphinmainwindow.moc" diff --git a/src/dolphinpart.cpp b/src/dolphinpart.cpp index 299043595..736ec6f4f 100644 --- a/src/dolphinpart.cpp +++ b/src/dolphinpart.cpp @@ -1,646 +1,644 @@ /* This file is part of the KDE project Copyright (c) 2007 David Faure 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 "dolphinpart.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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" #include #include #include K_PLUGIN_FACTORY(DolphinPartFactory, registerPlugin();) K_EXPORT_PLUGIN(DolphinPartFactory("dolphinpart", "dolphin")) DolphinPart::DolphinPart(QWidget* parentWidget, QObject* parent, const QVariantList& args) : KParts::ReadOnlyPart(parent) { Q_UNUSED(args) setComponentData(DolphinPartFactory::componentData(), false); m_extension = new DolphinPartBrowserExtension(this); new DolphinPartFileInfoExtension(this); // make sure that other apps using this part find Dolphin's view-file-columns icons KIconLoader::global()->addAppDir("dolphin"); m_view = new DolphinView(KUrl(), parentWidget); m_view->setTabsForFilesEnabled(true); setWidget(m_view); connect(m_view, SIGNAL(finishedPathLoading(KUrl)), this, SLOT(slotCompleted(KUrl))); connect(m_view, SIGNAL(pathLoadingProgress(int)), this, SLOT(updateProgress(int))); connect(m_view, SIGNAL(errorMessage(QString)), this, SLOT(slotErrorMessage(QString))); setXMLFile("dolphinpart.rc"); connect(m_view, SIGNAL(infoMessage(QString)), this, SLOT(slotMessage(QString))); connect(m_view, SIGNAL(operationCompletedMessage(QString)), this, SLOT(slotMessage(QString))); connect(m_view, SIGNAL(errorMessage(QString)), this, SLOT(slotErrorMessage(QString))); connect(m_view, SIGNAL(itemTriggered(KFileItem)), this, SLOT(slotItemTriggered(KFileItem))); connect(m_view, SIGNAL(tabRequested(KUrl)), this, SLOT(createNewWindow(KUrl))); connect(m_view, SIGNAL(requestContextMenu(KFileItem,KUrl,QList)), this, SLOT(slotOpenContextMenu(KFileItem,KUrl,QList))); connect(m_view, SIGNAL(selectionChanged(KFileItemList)), m_extension, SIGNAL(selectionInfo(KFileItemList))); connect(m_view, SIGNAL(selectionChanged(KFileItemList)), 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))); // Watch for changes that should result in updates to the // status bar text. connect(m_view, SIGNAL(itemCountChanged()), this, SLOT(updateStatusBar())); connect(m_view, SIGNAL(selectionChanged(const KFileItemList)), this, SLOT(updateStatusBar())); m_actionHandler = new DolphinViewActionHandler(actionCollection(), this); m_actionHandler->setCurrentView(m_view); connect(m_actionHandler, SIGNAL(createDirectory()), SLOT(createDirectory())); m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler); connect(this, SIGNAL(aboutToOpenURL()), m_remoteEncoding, SLOT(slotAboutToOpenUrl())); QClipboard* clipboard = QApplication::clipboard(); connect(clipboard, SIGNAL(dataChanged()), this, SLOT(updatePasteAction())); createActions(); m_actionHandler->updateViewActions(); slotSelectionChanged(KFileItemList()); // initially disable selection-dependent actions // TODO there was a "always open a new window" (when clicking on a directory) setting in konqueror // (sort of spacial navigation) loadPlugins(this, this, componentData()); } DolphinPart::~DolphinPart() { DolphinSettings::instance().save(); DolphinNewFileMenuObserver::instance().detach(m_newFileMenu); } void DolphinPart::createActions() { // Edit menu m_newFileMenu = new KNewFileMenu(actionCollection(), "new_menu", this); m_newFileMenu->setParentWidget(widget()); DolphinNewFileMenuObserver::instance().attach(m_newFileMenu); connect(m_newFileMenu->menu(), SIGNAL(aboutToShow()), this, SLOT(updateNewMenu())); KAction *editMimeTypeAction = actionCollection()->addAction( "editMimeType" ); editMimeTypeAction->setText( i18nc("@action:inmenu Edit", "&Edit File Type..." ) ); connect(editMimeTypeAction, SIGNAL(triggered()), SLOT(slotEditMimeType())); KAction* selectItemsMatching = actionCollection()->addAction("select_items_matching"); selectItemsMatching->setText(i18nc("@action:inmenu Edit", "Select Items Matching...")); selectItemsMatching->setShortcut(Qt::CTRL | Qt::Key_S); connect(selectItemsMatching, SIGNAL(triggered()), this, SLOT(slotSelectItemsMatchingPattern())); KAction* unselectItemsMatching = actionCollection()->addAction("unselect_items_matching"); unselectItemsMatching->setText(i18nc("@action:inmenu Edit", "Unselect Items Matching...")); connect(unselectItemsMatching, SIGNAL(triggered()), this, SLOT(slotUnselectItemsMatchingPattern())); actionCollection()->addAction(KStandardAction::SelectAll, "select_all", m_view, SLOT(selectAll())); KAction* unselectAll = actionCollection()->addAction("unselect_all"); unselectAll->setText(i18nc("@action:inmenu Edit", "Unselect All")); connect(unselectAll, SIGNAL(triggered()), m_view, SLOT(clearSelection())); KAction* invertSelection = actionCollection()->addAction("invert_selection"); invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection")); invertSelection->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_A); connect(invertSelection, SIGNAL(triggered()), m_view, SLOT(invertSelection())); // View menu: all done by DolphinViewActionHandler // Go menu QActionGroup* goActionGroup = new QActionGroup(this); connect(goActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotGoTriggered(QAction*))); createGoAction("go_applications", "start-here-kde", i18nc("@action:inmenu Go", "App&lications"), QString("programs:/"), goActionGroup); createGoAction("go_network_folders", "folder-remote", i18nc("@action:inmenu Go", "&Network Folders"), QString("remote:/"), goActionGroup); createGoAction("go_settings", "preferences-system", i18nc("@action:inmenu Go", "Sett&ings"), QString("settings:/"), goActionGroup); createGoAction("go_trash", "user-trash", i18nc("@action:inmenu Go", "Trash"), QString("trash:/"), goActionGroup); createGoAction("go_autostart", "", i18nc("@action:inmenu Go", "Autostart"), KGlobalSettings::autostartPath(), goActionGroup); // Tools menu KAction* findFile = actionCollection()->addAction("find_file"); findFile->setText(i18nc("@action:inmenu Tools", "Find File...")); findFile->setShortcut(Qt::CTRL | Qt::Key_F); findFile->setIcon(KIcon("edit-find")); connect(findFile, SIGNAL(triggered()), this, SLOT(slotFindFile())); if (KAuthorized::authorizeKAction("shell_access")) { KAction* action = actionCollection()->addAction("open_terminal"); action->setIcon(KIcon("utilities-terminal")); action->setText(i18nc("@action:inmenu Tools", "Open &Terminal")); connect(action, SIGNAL(triggered()), SLOT(slotOpenTerminal())); action->setShortcut(Qt::Key_F4); } } void DolphinPart::createGoAction(const char* name, const char* iconName, const QString& text, const QString& url, QActionGroup* actionGroup) { KAction* action = actionCollection()->addAction(name); action->setIcon(KIcon(iconName)); action->setText(text); action->setData(url); action->setActionGroup(actionGroup); } void DolphinPart::slotGoTriggered(QAction* action) { const QString url = action->data().toString(); emit m_extension->openUrlRequest(KUrl(url)); } void DolphinPart::slotSelectionChanged(const KFileItemList& selection) { const bool hasSelection = !selection.isEmpty(); QAction* renameAction = actionCollection()->action("rename"); QAction* moveToTrashAction = actionCollection()->action("move_to_trash"); QAction* deleteAction = actionCollection()->action("delete"); QAction* editMimeTypeAction = actionCollection()->action("editMimeType"); QAction* propertiesAction = actionCollection()->action("properties"); QAction* deleteWithTrashShortcut = actionCollection()->action("delete_shortcut"); // see DolphinViewActionHandler if (!hasSelection) { stateChanged("has_no_selection"); emit m_extension->enableAction("cut", false); emit m_extension->enableAction("copy", false); deleteWithTrashShortcut->setEnabled(false); editMimeTypeAction->setEnabled(false); } else { stateChanged("has_selection"); // TODO share this code with DolphinMainWindow::updateEditActions (and the desktop code) // in libkonq KFileItemListProperties capabilities(selection); const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving(); renameAction->setEnabled(capabilities.supportsMoving()); moveToTrashAction->setEnabled(enableMoveToTrash); deleteAction->setEnabled(capabilities.supportsDeleting()); deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash); editMimeTypeAction->setEnabled(true); propertiesAction->setEnabled(true); emit m_extension->enableAction("cut", capabilities.supportsMoving()); emit m_extension->enableAction("copy", true); } } void DolphinPart::updatePasteAction() { QPair pasteInfo = m_view->pasteInfo(); emit m_extension->enableAction( "paste", pasteInfo.first ); emit m_extension->setActionText( "paste", pasteInfo.second ); } KAboutData* DolphinPart::createAboutData() { return new KAboutData("dolphinpart", "dolphin", ki18nc("@title", "Dolphin Part"), "0.1"); } bool DolphinPart::openUrl(const KUrl& url) { bool reload = arguments().reload(); // A bit of a workaround so that changing the namefilter works: force reload. // Otherwise DolphinView wouldn't relist the URL, so nothing would happen. if (m_nameFilter != m_view->nameFilter()) reload = true; if (m_view->url() == url && !reload) { // DolphinView won't do anything in that case, so don't emit started return true; } setUrl(url); // remember it at the KParts level KUrl visibleUrl(url); if (!m_nameFilter.isEmpty()) { visibleUrl.addPath(m_nameFilter); } QString prettyUrl = visibleUrl.pathOrUrl(); emit setWindowCaption(prettyUrl); emit m_extension->setLocationBarUrl(prettyUrl); emit started(0); // get the wheel to spin m_view->setNameFilter(m_nameFilter); m_view->setUrl(url); updatePasteAction(); emit aboutToOpenURL(); if (reload) m_view->reload(); return true; } void DolphinPart::slotCompleted(const KUrl& url) { Q_UNUSED(url) emit completed(); } void DolphinPart::slotMessage(const QString& msg) { emit setStatusBarText(msg); } void DolphinPart::slotErrorMessage(const QString& msg) { kDebug() << msg; emit canceled(msg); //KMessageBox::error(m_view, msg); } void DolphinPart::slotRequestItemInfo(const KFileItem& item) { emit m_extension->mouseOverInfo(item); if (item.isNull()) { updateStatusBar(); } else { ReadOnlyPart::setStatusBarText(item.getStatusBarInfo()); } } void DolphinPart::slotItemTriggered(const KFileItem& item) { KParts::OpenUrlArguments args; // Forget about the known mimetype if a target URL is used. // Testcase: network:/ with a item (mimetype "inode/some-foo-service") pointing to a http URL (html) if (item.targetUrl() == item.url()) { args.setMimeType(item.mimetype()); } // Ideally, konqueror should be changed to not require trustedSource for directory views, // since the idea was not to need BrowserArguments for non-browser stuff... KParts::BrowserArguments browserArgs; browserArgs.trustedSource = true; emit m_extension->openUrlRequest(item.targetUrl(), args, browserArgs); } void DolphinPart::createNewWindow(const KUrl& url) { // TODO: Check issue N176832 for the missing QAIV signal; task 177399 - maybe this code // should be moved into DolphinPart::slotItemTriggered() emit m_extension->createNewWindow(url); } void DolphinPart::slotOpenContextMenu(const KFileItem& _item, const KUrl&, const QList& customActions) { KParts::BrowserExtension::PopupFlags popupFlags = KParts::BrowserExtension::DefaultPopupItems | KParts::BrowserExtension::ShowProperties | KParts::BrowserExtension::ShowUrlOperations; KFileItem item(_item); if (item.isNull()) { // viewport context menu popupFlags |= KParts::BrowserExtension::ShowNavigationItems | KParts::BrowserExtension::ShowUp; item = m_view->rootItem(); if (item.isNull()) item = KFileItem( S_IFDIR, (mode_t)-1, url() ); else item.setUrl(url()); // ensure we use the view url, not the canonical path (#213799) } // TODO: We should change the signature of the slots (and signals) for being able // to tell for which items we want a popup. KFileItemList items; if (m_view->selectedItems().isEmpty()) { items.append(item); } else { items = m_view->selectedItems(); } KFileItemListProperties capabilities(items); KParts::BrowserExtension::ActionGroupMap actionGroups; QList editActions; editActions += m_view->versionControlActions(m_view->selectedItems()); editActions += customActions; if (!_item.isNull()) { // only for context menu on one or more items bool supportsDeleting = capabilities.supportsDeleting(); bool supportsMoving = capabilities.supportsMoving(); if (!supportsDeleting) { popupFlags |= KParts::BrowserExtension::NoDeletion; } if (supportsMoving) { editActions.append(actionCollection()->action("rename")); } bool addTrash = capabilities.isLocal() && supportsMoving; bool addDel = false; if (supportsDeleting) { if ( !item.isLocalFile() ) addDel = true; else if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { addTrash = false; addDel = true; } else { KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig("kdeglobals", KConfig::IncludeGlobals); KConfigGroup configGroup(globalConfig, "KDE"); addDel = configGroup.readEntry("ShowDeleteCommand", false); } } if (addTrash) editActions.append(actionCollection()->action("move_to_trash")); if (addDel) editActions.append(actionCollection()->action("delete")); // Normally KonqPopupMenu only shows the "Create new" submenu in the current view // since otherwise the created file would not be visible. // But in treeview mode we should allow it. if (m_view->itemsExpandable()) popupFlags |= KParts::BrowserExtension::ShowCreateDirectory; } actionGroups.insert("editactions", editActions); emit m_extension->popupMenu(QCursor::pos(), items, KParts::OpenUrlArguments(), KParts::BrowserArguments(), popupFlags, actionGroups); } void DolphinPart::slotRedirection(const KUrl& oldUrl, const KUrl& newUrl) { //kDebug() << oldUrl << newUrl << "currentUrl=" << url(); if (oldUrl.equals(url(), KUrl::CompareWithoutTrailingSlash /* #207572 */)) { KParts::ReadOnlyPart::setUrl(newUrl); const QString prettyUrl = newUrl.pathOrUrl(); emit m_extension->setLocationBarUrl(prettyUrl); } } void DolphinPart::slotEditMimeType() { const KFileItemList items = m_view->selectedItems(); if (!items.isEmpty()) { KonqOperations::editMimeType(items.first().mimetype(), m_view); } } void DolphinPart::slotSelectItemsMatchingPattern() { openSelectionDialog(i18nc("@title:window", "Select"), i18n("Select all items matching this pattern:"), true); } void DolphinPart::slotUnselectItemsMatchingPattern() { openSelectionDialog(i18nc("@title:window", "Unselect"), i18n("Unselect all items matching this pattern:"), false); } void DolphinPart::openSelectionDialog(const QString& title, const QString& text, bool selectItems) { bool okClicked; QString pattern = KInputDialog::getText(title, text, "*", &okClicked, m_view); if (okClicked && !pattern.isEmpty()) { QRegExp patternRegExp(pattern, Qt::CaseSensitive, QRegExp::Wildcard); m_view->setItemSelectionEnabled(patternRegExp, selectItems); } } void DolphinPart::setCurrentViewMode(const QString& viewModeName) { QAction* action = actionCollection()->action(viewModeName); Q_ASSERT(action); action->trigger(); } QString DolphinPart::currentViewMode() const { return m_actionHandler->currentViewModeActionName(); } void DolphinPart::setNameFilter(const QString& nameFilter) { // This is the "/home/dfaure/*.diff" kind of name filter (KDirLister::setNameFilter) // which is unrelated to DolphinView::setNameFilter which is substring filtering in a proxy. m_nameFilter = nameFilter; // TODO save/restore name filter in saveState/restoreState like KonqDirPart did in kde3? } void DolphinPart::slotOpenTerminal() { QString dir(QDir::homePath()); KUrl u(url()); // If the given directory is not local, it can still be the URL of an // ioslave using UDS_LOCAL_PATH which to be converted first. u = KIO::NetAccess::mostLocalUrl(u, widget()); //If the URL is local after the above conversion, set the directory. if (u.isLocalFile()) { dir = u.toLocalFile(); } KToolInvocation::invokeTerminal(QString(), dir); } void DolphinPart::slotFindFile() { KRun::run("kfind", url(), widget()); } 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()); } void DolphinPart::updateStatusBar() { emit ReadOnlyPart::setStatusBarText(m_view->statusBarText()); } void DolphinPart::updateProgress(int percent) { m_extension->loadingProgress(percent); } void DolphinPart::createDirectory() { - m_newFileMenu->setViewShowsHiddenFiles(m_view->showHiddenFiles()); + m_newFileMenu->setViewShowsHiddenFiles(m_view->hiddenFilesShown()); m_newFileMenu->setPopupFiles(url()); m_newFileMenu->createDirectory(); } void DolphinPart::setFilesToSelect(const KUrl::List& files) { m_view->markUrlsAsSelected(files); } //// void DolphinPartBrowserExtension::restoreState(QDataStream &stream) { KParts::BrowserExtension::restoreState(stream); m_part->view()->restoreState(stream); } void DolphinPartBrowserExtension::saveState(QDataStream &stream) { KParts::BrowserExtension::saveState(stream); m_part->view()->saveState(stream); } void DolphinPartBrowserExtension::cut() { m_part->view()->cutSelectedItems(); } void DolphinPartBrowserExtension::copy() { m_part->view()->copySelectedItems(); } void DolphinPartBrowserExtension::paste() { m_part->view()->paste(); } void DolphinPartBrowserExtension::pasteTo(const KUrl&) { m_part->view()->pasteIntoFolder(); } void DolphinPartBrowserExtension::reparseConfiguration() { m_part->view()->refresh(); } //// DolphinPartFileInfoExtension::DolphinPartFileInfoExtension(DolphinPart* part) : KParts::FileInfoExtension(part) { } DolphinPart* DolphinPartFileInfoExtension::part() const { return static_cast(parent()); } bool DolphinPartFileInfoExtension::hasSelection() const { return part()->view()->hasSelection(); } KParts::FileInfoExtension::QueryModes DolphinPartFileInfoExtension::supportedQueryModes() const { return (KParts::FileInfoExtension::AllItems | KParts::FileInfoExtension::SelectedItems); } KFileItemList DolphinPartFileInfoExtension::queryFor(KParts::FileInfoExtension::QueryMode mode) const { KFileItemList list; if (mode == KParts::FileInfoExtension::None) return list; if (!(supportedQueryModes() & mode)) return list; switch (mode) { case KParts::FileInfoExtension::SelectedItems: if (hasSelection()) return part()->view()->selectedItems(); break; case KParts::FileInfoExtension::AllItems: return part()->view()->items(); default: break; } return list; } #include "dolphinpart.moc" diff --git a/src/dolphinpart.desktop b/src/dolphinpart.desktop index 511f7f900..13dfacf47 100644 --- a/src/dolphinpart.desktop +++ b/src/dolphinpart.desktop @@ -1,376 +1,376 @@ [Desktop Entry] Type=Service Name=Dolphin View Name[af]=Dolphin Deel Name[ar]=عرض دولفين Name[as]=Dolphin প্ৰদৰ্শন Name[ast]=Vista de Dolphin Name[be@latin]=Vyhlad „Dolphin” Name[bg]=Преглед в Dolphin Name[bn]=ডলফিন ভিউ Name[bn_IN]=Dolphin প্রদর্শন Name[bs]=Delfinov prikaz Name[ca]=Vista del Dolphin Name[ca@valencia]=Vista del Dolphin Name[cs]=Pohled Dolphin Name[csb]=Wëzdrzatk Dolphina Name[da]=Dolphin-visning Name[de]=Dolphin-Ansicht Name[el]=Προβολή του Dolphin Name[en_GB]=Dolphin View Name[eo]=Dolphin-rigardo Name[es]=Vista de Dolphin Name[et]=Dolphini vaade Name[eu]=Dolphin ikuspegia Name[fi]=Dolphin-näkymä Name[fr]=Vue de Dolphin Name[fy]=Dolfyn werjefte Name[ga]=Amharc Dolphin Name[gl]=Vista de Dolphin Name[gu]=ડોલ્ફિન દેખાવ Name[he]=הגדרות תצוגה ב־Dolphin Name[hi]=डॉल्फ़िन दृश्य Name[hne]=डाल्फिन दृस्य Name[hr]=Dolphinov prikaz Name[hsb]=Napohlad w Dolphinje Name[hu]=Dolphin-nézet Name[ia]=Vista de Dolphin Name[id]=Tampilan Dolphin Name[is]=Dolphin sýn Name[it]=Vista di Dolphin Name[ja]=Dolphin ビュー Name[kk]=Dolphin көрінісі Name[km]=ទិដ្ឋភាព Dolphin Name[kn]=ಡಾಲ್ಫಿನ್ ನೋಟ Name[ko]=Dolphin 보기 Name[ku]=Bergehê Dolphin Name[lt]=Dolphin žiūryklė Name[lv]=Dolphin skats Name[mai]=डाल्फिन दृश्य Name[mk]=Преглед со Делфин Name[ml]=ഡോള്‍ഫിന്‍ അവതരണരീതി Name[mr]=डॉल्फिन दृश्य Name[ms]=Lihat Dolphin Name[nb]=Dolphin visning Name[nds]=Dolphin-Ansicht Name[nl]=Dolphin-weergave Name[nn]=Dolphin-vising Name[or]=ଡଲଫିନ ଦୃଶ୍ୟ Name[pa]=ਡਾਲਫਿਨ ਝਲਕ Name[pl]=Widok Dolphina Name[pt]=Área do Dolphin Name[pt_BR]=Visualização do Dolphin Name[ro]=Vizualizare Dolphin Name[ru]=Представление Dolphin Name[se]=Dolphinčájeheapmi Name[si]=ඩොල්ෆින් දසුන Name[sk]=Dolphin pohľad Name[sl]=Dolphin - pogled Name[sr]=Делфинов приказ Name[sr@ijekavian]=Делфинов приказ Name[sr@ijekavianlatin]=Dolphinov prikaz Name[sr@latin]=Dolphinov prikaz Name[sv]=Vy i Dolphin Name[ta]=டால்பின் காட்சி Name[te]=డాల్ఫిన్ వీక్షణం Name[tg]=Намоиши Dolphin Name[th]=มุมมองของดอลฟิน Name[tr]=Dolphin Görünümü Name[ug]=Dolphin كۆرۈنۈش Name[uk]=Перегляд Dolphin Name[wa]=Vuwe di Dolphin Name[x-test]=xxDolphin Viewxx Name[zh_CN]=Dolphin 视图 Name[zh_TW]=Dolphin 檢視 MimeType=inode/directory; X-KDE-ServiceTypes=KParts/ReadOnlyPart,Browser/View X-KDE-Library=dolphinpart #X-KDE-BrowserView-Args=Icon X-KDE-BrowserView-HideFromMenus=true X-KDE-BrowserView-Built-Into=konqueror Icon=view-icon InitialPreference=7 # Provide info about the view modes using the Actions mechanism so that KService parses it. # Konqueror then queries KService to get hold of the translated texts for the view modes Actions=icons;details;columns; [Desktop Action icons] Name=Icons Name[af]=Ikone Name[ar]=الأيقونات Name[as]=আইকন Name[ast]=Iconos Name[be]=Значкі Name[be@latin]=Ikony Name[bg]=Икони Name[bn]=আইকন Name[bn_IN]=আইকন Name[br]=Arlunioù Name[bs]=Ikone Name[ca]=Icones Name[ca@valencia]=Icones Name[cs]=Ikony Name[csb]=Ikònë Name[cy]=Eicon Name[da]=Ikoner Name[de]=Symbole Name[el]=Εικονίδια Name[en_GB]=Icons Name[eo]=Piktogramoj Name[es]=Iconos Name[et]=Ikoonid Name[eu]=Ikonoak Name[fa]=شمایلها Name[fi]=Kuvakkeet Name[fr]=Icônes Name[fy]=Byldkaikes Name[ga]=Deilbhíní Name[gl]=Iconas Name[gu]=ચિહ્નો Name[he]=סמלים Name[hi]=प्रतीक Name[hne]=चिनहा Name[hr]=Ikone Name[hsb]=Piktogramy Name[hu]=Ikonok Name[ia]=Icones Name[id]=Ikon Name[is]=Táknmyndir Name[it]=Icone Name[ja]=アイコン Name[ka]=ხატულები Name[kk]=Таңбашалар Name[km]=រូប​តំណាង Name[kn]=ಚಿಹ್ನೆಗಳು Name[ko]=아이콘 Name[ku]=Îkon Name[lt]=Ženkliukai Name[lv]=Ikonas Name[mai]=प्रतीक Name[mk]=Икони Name[ml]=ചിഹ്നങ്ങള്‍ Name[mr]=चिन्ह Name[ms]=Ikon Name[nb]=Ikoner Name[nds]=Lüttbiller Name[ne]=प्रतिमा Name[nl]=Pictogrammen Name[nn]=Ikon Name[oc]=Icònas Name[or]=ଚିତ୍ର ସଂକେତଗୁଡ଼ିକ Name[pa]=ਆਈਕਾਨ Name[pl]=Ikony Name[pt]=Ícones Name[pt_BR]=Ícones Name[ro]=Pictograme Name[ru]=Значки Name[se]=Govažat Name[si]=අයිකන Name[sk]=Ikony Name[sl]=Ikone Name[sr]=Иконе Name[sr@ijekavian]=Иконе Name[sr@ijekavianlatin]=Ikone Name[sr@latin]=Ikone Name[sv]=Ikoner Name[ta]=சின்னங்கள் Name[te]=ప్రతిమలు Name[tg]=Нишонаҳо Name[th]=ไอคอน Name[tr]=Simgeler Name[ug]=سىنبەلگىلەر Name[uk]=Піктограми Name[uz]=Nishonchalar Name[uz@cyrillic]=Нишончалар Name[vi]=Biểu tượng Name[wa]=Imådjetes Name[xh]=Imphawu zemmifanekiso Name[x-test]=xxIconsxx Name[zh_CN]=图标 Name[zh_TW]=圖示 Icon=view-list-icons # Dummy Exec=dolphin [Desktop Action details] Name=Details Name[af]=Besonderhede Name[ar]=تفاصيل Name[as]=বিৱৰণ Name[ast]=Detalles Name[be@latin]=Detali Name[bg]=Подробности Name[bn]=বিস্তারিত Name[bn_IN]=বিবরণ Name[bs]=Detalji Name[ca]=Detalls Name[ca@valencia]=Detalls Name[cs]=Detaily Name[csb]=Detale Name[da]=Detaljer Name[de]=Details Name[el]=Λεπτομέρειες Name[en_GB]=Details Name[eo]=Detaloj Name[es]=Detalles Name[et]=Üksikasjad Name[eu]=Xehetasunak Name[fi]=Yksityiskohdat Name[fr]=Détails Name[fy]=Details Name[ga]=Mionsonraí Name[gl]=Detalles Name[gu]=વિગતો Name[he]=פרטים Name[hi]=विवरण Name[hne]=विवरन Name[hr]=Detalji Name[hsb]=Nadrobnosće Name[hu]=Részletek Name[ia]=Detalios Name[id]=Detail Name[is]=Smáatriði Name[it]=Dettagli Name[ja]=詳細 Name[ka]=დეტალები Name[kk]=Егжей-тегжейі Name[km]=សេចក្ដី​លម្អិត Name[kn]=ವಿವರಗಳು Name[ko]=자세히 Name[ku]=Kitekit Name[lt]=Detalės Name[lv]=Detaļas Name[mai]=विवरण Name[mk]=Детали Name[ml]=വിശദവിവരങ്ങള്‍ Name[mr]=तपशील Name[ms]=Perincian Name[nb]=Detaljer Name[nds]=Enkelheiten Name[nl]=Details Name[nn]=Detaljar Name[oc]=Detalhs Name[or]=ବିସ୍ତୃତ ବିବରଣୀ Name[pa]=ਵੇਰਵਾ Name[pl]=Szczegóły Name[pt]=Detalhes Name[pt_BR]=Detalhes Name[ro]=Detalii Name[ru]=Сведения Name[si]=විස්තර Name[sk]=Podrobnosti Name[sl]=Podrobnosti Name[sr]=Детаљи Name[sr@ijekavian]=Детаљи Name[sr@ijekavianlatin]=Detalji Name[sr@latin]=Detalji Name[sv]=Detaljinformation Name[ta]=விவரங்கள் Name[te]=వివరాలు Name[tg]=Тафсилотҳо Name[th]=รายละเอียด Name[tr]=Ayrıntılar -Name[ug]=تەپسىلاتى +Name[ug]=تەپسىلاتلار Name[uk]=Подробиці Name[uz]=Tafsilotlar Name[uz@cyrillic]=Тафсилотлар Name[wa]=Detays Name[x-test]=xxDetailsxx Name[zh_CN]=细节 Name[zh_TW]=詳情 Icon=view-list-details # Dummy Exec=dolphin [Desktop Action columns] Name=Columns Name[af]=Kolomme Name[ar]=الأعمدة Name[as]=কলাম Name[ast]=Columnes Name[be@latin]=Słupki Name[bg]=Колони Name[bn]=কলাম Name[bn_IN]=কলাম Name[bs]=Kolone Name[ca]=Columnes Name[ca@valencia]=Columnes Name[cs]=Sloupce Name[csb]=Kòlumnë Name[da]=Kolonner Name[de]=Spalten Name[el]=Στήλες Name[en_GB]=Columns Name[eo]=Kolumnoj Name[es]=Columnas Name[et]=Veerud Name[eu]=Zutabeak Name[fa]=ستونها Name[fi]=Sarakkeet Name[fr]=Colonnes Name[fy]=Kolommen Name[ga]=Colúin Name[gl]=Columnas Name[gu]=સ્થંભો Name[he]=עמודות Name[hi]=स्तम्भ Name[hne]=स्तम्भ Name[hr]=Stupci Name[hsb]=Stołpiki Name[hu]=Oszlopok Name[ia]=Columnas Name[id]=Kolom Name[is]=Dálkar Name[it]=Colonne Name[ja]=カラム Name[ka]=სვეტები Name[kk]=Бағандар Name[km]=ជួរឈរ Name[kn]= ಲಂಬಸಾಲುಗಳ ಬಣ್ಣಗಳು Name[ko]=열 Name[ku]=Stûn Name[lt]=Stulpeliai Name[lv]=Kolonas Name[mai]=कालम Name[mk]=Колони Name[ml]=നിരകള്‍ Name[mr]=स्तंभ Name[ms]=Lajur Name[nb]=Kolonner Name[nds]=Striepen Name[nl]=Kolommen Name[nn]=Kolonnar Name[or]=ସ୍ତମ୍ଭ Name[pa]=ਕਾਲਮ Name[pl]=Kolumny Name[pt]=Colunas Name[pt_BR]=Colunas Name[ro]=Coloane Name[ru]=Столбцы Name[se]=Čuolddat Name[si]=තීරු Name[sk]=Stĺpce Name[sl]=Stolpci Name[sr]=Колоне Name[sr@ijekavian]=Колоне Name[sr@ijekavianlatin]=Kolone Name[sr@latin]=Kolone Name[sv]=Kolumner Name[ta]=நெடுவரிசைகள் Name[te]=నిలువు వరుసలు Name[tg]=Сутунҳо Name[th]=คอลัมน์ Name[tr]=Sütunlar Name[ug]=ئىستونلار Name[uk]=Колонки Name[uz]=Ustunlar Name[uz@cyrillic]=Устунлар Name[wa]=Colones Name[x-test]=xxColumnsxx Name[zh_CN]=分列 Name[zh_TW]=欄位 Icon=view-file-columns # Dummy Exec=dolphin diff --git a/src/dolphinui.rc b/src/dolphinui.rc index 82803cc1b..68e03752f 100644 --- a/src/dolphinui.rc +++ b/src/dolphinui.rc @@ -1,115 +1,115 @@ Location Bar Main Toolbar + - + - diff --git a/src/dolphinviewcontainer.cpp b/src/dolphinviewcontainer.cpp index 67ea84d1b..8e00b0afc 100644 --- a/src/dolphinviewcontainer.cpp +++ b/src/dolphinviewcontainer.cpp @@ -1,590 +1,610 @@ /*************************************************************************** * Copyright (C) 2007 by Peter Penz * * * * 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 "dolphinviewcontainer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dolphin_generalsettings.h" #include "dolphinmainwindow.h" #include "filterbar/filterbar.h" #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" DolphinViewContainer::DolphinViewContainer(const KUrl& url, QWidget* parent) : QWidget(parent), m_topLayout(0), m_urlNavigator(0), m_searchBox(0), m_view(0), m_filterBar(0), m_statusBar(0), m_statusBarTimer(0), m_statusBarTimestamp() { hide(); m_topLayout = new QVBoxLayout(this); m_topLayout->setSpacing(0); m_topLayout->setMargin(0); m_urlNavigator = new KUrlNavigator(DolphinSettings::instance().placesModel(), url, this); connect(m_urlNavigator, SIGNAL(urlsDropped(const KUrl&, QDropEvent*)), this, SLOT(dropUrls(const KUrl&, QDropEvent*))); connect(m_urlNavigator, SIGNAL(activated()), this, SLOT(activate())); connect(m_urlNavigator->editor(), SIGNAL(completionModeChanged(KGlobalSettings::Completion)), this, SLOT(saveUrlCompletionMode(KGlobalSettings::Completion))); const GeneralSettings* settings = DolphinSettings::instance().generalSettings(); m_urlNavigator->setUrlEditable(settings->editableUrl()); m_urlNavigator->setShowFullPath(settings->showFullPath()); m_urlNavigator->setHomeUrl(KUrl(settings->homeUrl())); KUrlComboBox* editor = m_urlNavigator->editor(); editor->setCompletionMode(KGlobalSettings::Completion(settings->urlCompletionMode())); m_searchBox = new DolphinSearchBox(this); m_searchBox->hide(); connect(m_searchBox, SIGNAL(closeRequest()), this, SLOT(closeSearchBox())); connect(m_searchBox, SIGNAL(search(QString)), this, SLOT(startSearching(QString))); connect(m_searchBox, SIGNAL(returnPressed(QString)), this, SLOT(requestFocus())); m_view = new DolphinView(url, this); connect(m_view, SIGNAL(urlChanged(const KUrl&)), m_urlNavigator, SLOT(setUrl(const KUrl&))); connect(m_view, SIGNAL(writeStateChanged(bool)), this, SIGNAL(writeStateChanged(bool))); connect(m_view, SIGNAL(requestItemInfo(KFileItem)), this, SLOT(showItemInfo(KFileItem))); connect(m_view, SIGNAL(errorMessage(const QString&)), this, SLOT(showErrorMessage(const QString&))); connect(m_view, SIGNAL(infoMessage(const QString&)), this, SLOT(showInfoMessage(const QString&))); connect(m_view, SIGNAL(itemTriggered(KFileItem)), this, SLOT(slotItemTriggered(KFileItem))); connect(m_view, SIGNAL(redirection(KUrl, KUrl)), this, SLOT(redirect(KUrl, KUrl))); connect(m_view, SIGNAL(startedPathLoading(KUrl)), this, SLOT(slotStartedPathLoading())); connect(m_view, SIGNAL(finishedPathLoading(KUrl)), this, SLOT(slotFinishedPathLoading())); connect(m_view, SIGNAL(itemCountChanged()), this, SLOT(delayedStatusBarUpdate())); connect(m_view, SIGNAL(pathLoadingProgress(int)), this, SLOT(updateProgress(int))); connect(m_view, SIGNAL(infoMessage(const QString&)), this, SLOT(showInfoMessage(const QString&))); connect(m_view, SIGNAL(errorMessage(const QString&)), this, SLOT(showErrorMessage(const QString&))); 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())); // initialize status bar m_statusBar = new DolphinStatusBar(this, m_view); connect(m_statusBar, SIGNAL(stopPressed()), this, SLOT(stopLoading())); m_statusBarTimer = new QTimer(this); m_statusBarTimer->setSingleShot(true); m_statusBarTimer->setInterval(300); connect(m_statusBarTimer, SIGNAL(timeout()), this, SLOT(updateStatusBar())); KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self(); connect(undoManager, SIGNAL(jobRecordingFinished(CommandType)), this, SLOT(delayedStatusBarUpdate())); // initialize filter bar m_filterBar = new FilterBar(this); m_filterBar->setVisible(settings->filterBar()); connect(m_filterBar, SIGNAL(filterChanged(const QString&)), this, SLOT(setNameFilter(const QString&))); connect(m_filterBar, SIGNAL(closeRequest()), this, SLOT(closeFilterBar())); connect(m_view, SIGNAL(urlChanged(const KUrl&)), m_filterBar, SLOT(clear())); m_topLayout->addWidget(m_urlNavigator); m_topLayout->addWidget(m_searchBox); m_topLayout->addWidget(m_view); m_topLayout->addWidget(m_filterBar); m_topLayout->addWidget(m_statusBar); setSearchModeEnabled(isSearchUrl(url)); } DolphinViewContainer::~DolphinViewContainer() { } KUrl DolphinViewContainer::url() const { return m_view->url(); } void DolphinViewContainer::setActive(bool active) { m_urlNavigator->setActive(active); m_view->setActive(active); } bool DolphinViewContainer::isActive() const { Q_ASSERT(m_view->isActive() == m_urlNavigator->isActive()); return m_view->isActive(); } const DolphinStatusBar* DolphinViewContainer::statusBar() const { return m_statusBar; } DolphinStatusBar* DolphinViewContainer::statusBar() { return m_statusBar; } const KUrlNavigator* DolphinViewContainer::urlNavigator() const { return m_urlNavigator; } KUrlNavigator* DolphinViewContainer::urlNavigator() { return m_urlNavigator; } const DolphinView* DolphinViewContainer::view() const { return m_view; } DolphinView* DolphinViewContainer::view() { return m_view; } const DolphinSearchBox* DolphinViewContainer::searchBox() const { return m_searchBox; } DolphinSearchBox* DolphinViewContainer::searchBox() { return m_searchBox; } void DolphinViewContainer::refresh() { GeneralSettings* settings = DolphinSettings::instance().generalSettings(); if (settings->modifiedStartupSettings()) { // The startup settings should only get applied if they have been // modified by the user. Otherwise keep the (possibly) different current // settings of the URL navigator and the filterbar. m_urlNavigator->setUrlEditable(settings->editableUrl()); m_urlNavigator->setShowFullPath(settings->showFullPath()); setFilterBarVisible(settings->filterBar()); } m_view->refresh(); m_statusBar->refresh(); } bool DolphinViewContainer::isFilterBarVisible() const { return m_filterBar->isVisible(); } void DolphinViewContainer::setSearchModeEnabled(bool enabled) { if (enabled == isSearchModeEnabled()) { if (enabled && !m_searchBox->hasFocus()) { m_searchBox->setFocus(); m_searchBox->selectAll(); } return; } m_searchBox->setVisible(enabled); m_urlNavigator->setVisible(!enabled); if (enabled) { KUrl url = m_urlNavigator->locationUrl(); m_searchBox->setText(QString()); m_searchBox->setReadOnly(isSearchUrl(url), url); // Remember the most recent non-search URL as search path // of the search-box, so that it can be restored // when switching back to the URL navigator. int index = m_urlNavigator->historyIndex(); const int historySize = m_urlNavigator->historySize(); while (isSearchUrl(url) && (index < historySize)) { ++index; url = m_urlNavigator->locationUrl(index); } if (!isSearchUrl(url)) { m_searchBox->setSearchPath(url); } } else { // Restore the URL for the URL navigator. If Dolphin has been // started with a search-URL, the home URL is used as fallback. const KUrl url = m_searchBox->searchPath(); if (url.isValid() && !url.isEmpty()) { if (isSearchUrl(url)) { m_urlNavigator->goHome(); } else { m_urlNavigator->setLocationUrl(url); } } } emit searchModeChanged(enabled); } bool DolphinViewContainer::isSearchModeEnabled() const { return m_searchBox->isVisible(); } void DolphinViewContainer::setUrl(const KUrl& newUrl) { if (newUrl != m_urlNavigator->locationUrl()) { m_urlNavigator->setLocationUrl(newUrl); } } void DolphinViewContainer::setFilterBarVisible(bool visible) { Q_ASSERT(m_filterBar); if (visible) { m_filterBar->show(); m_filterBar->setFocus(); m_filterBar->selectAll(); } else { closeFilterBar(); } } void DolphinViewContainer::delayedStatusBarUpdate() { if (m_statusBarTimer->isActive() && (m_statusBarTimestamp.elapsed() > 2000)) { // No update of the statusbar has been done during the last 2 seconds, // although an update has been requested. Trigger an immediate update. m_statusBarTimer->stop(); updateStatusBar(); } else { // Invoke updateStatusBar() with a small delay. This assures that // when a lot of delayedStatusBarUpdates() are done in a short time, // no bottleneck is given. m_statusBarTimer->start(); } } void DolphinViewContainer::updateStatusBar() { m_statusBarTimestamp.start(); const QString newMessage = m_view->statusBarText(); m_statusBar->setDefaultText(newMessage); // We don't want to override errors. Other messages are only protected by // the Statusbar itself depending on timings (see DolphinStatusBar::setMessage). if (m_statusBar->type() != DolphinStatusBar::Error) { m_statusBar->setMessage(newMessage, DolphinStatusBar::Default); } } void DolphinViewContainer::updateProgress(int percent) { if (m_statusBar->progressText().isEmpty()) { m_statusBar->setProgressText(i18nc("@info:progress", "Loading folder...")); } m_statusBar->setProgress(percent); } void DolphinViewContainer::slotStartedPathLoading() { if (isSearchUrl(url())) { // Search KIO-slaves usually don't provide any progress information. Give // a hint to the user that a searching is done: updateStatusBar(); m_statusBar->setProgressText(i18nc("@info", "Searching...")); m_statusBar->setProgress(-1); } else { // Trigger an undetermined progress indication. The progress // information in percent will be triggered by the percent() signal // of the directory lister later. updateProgress(-1); } } void DolphinViewContainer::slotFinishedPathLoading() { if (!m_statusBar->progressText().isEmpty()) { m_statusBar->setProgressText(QString()); m_statusBar->setProgress(100); } if (isSearchUrl(url()) && m_view->items().isEmpty()) { // The dir lister has been completed on a Nepomuk-URI and no items have been found. Instead // of showing the default status bar information ("0 items") a more helpful information is given: m_statusBar->setMessage(i18nc("@info:status", "No items found."), DolphinStatusBar::Information); } else { updateStatusBar(); } } +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:// when clicking on a tar file, + // zip:// 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()) { // Only clear the status bar if unimportant messages are shown. // This prevents that information- or error-messages get hidden // by moving the mouse above the viewport or when closing the // context menu. if (m_statusBar->type() == DolphinStatusBar::Default) { m_statusBar->clear(); } } else { m_statusBar->setMessage(item.getStatusBarInfo(), DolphinStatusBar::Default); } } void DolphinViewContainer::showInfoMessage(const QString& msg) { m_statusBar->setMessage(msg, DolphinStatusBar::Information); } void DolphinViewContainer::showErrorMessage(const QString& msg) { m_statusBar->setMessage(msg, DolphinStatusBar::Error); } void DolphinViewContainer::showOperationCompletedMessage(const QString& msg) { m_statusBar->setMessage(msg, DolphinStatusBar::OperationCompleted); } void DolphinViewContainer::closeFilterBar() { m_filterBar->hide(); m_filterBar->clear(); m_view->setFocus(); emit showFilterBarChanged(false); } void DolphinViewContainer::setNameFilter(const QString& nameFilter) { m_view->setNameFilter(nameFilter); delayedStatusBarUpdate(); } 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 // a new view widget. QTimer::singleShot(0, this, SLOT(requestFocus())); } } else if (KProtocolManager::isSourceProtocol(url)) { QString app = "konqueror"; if (url.protocol().startsWith(QLatin1String("http"))) { showErrorMessage(i18nc("@info:status", "Dolphin does not support web pages, the web browser has been launched")); const KConfigGroup config(KSharedConfig::openConfig("kdeglobals"), "General"); const QString browser = config.readEntry("BrowserApplication"); if (!browser.isEmpty()) { app = browser; if (app.startsWith('!')) { // a literal command has been configured, remove the '!' prefix app = app.mid(1); } } } else { showErrorMessage(i18nc("@info:status", "Protocol not supported by Dolphin, Konqueror has been launched")); } const QString secureUrl = KShell::quoteArg(url.pathOrUrl()); const QString command = app + ' ' + secureUrl; KRun::runCommand(command, app, app, this); } else { showErrorMessage(i18nc("@info:status", "Invalid protocol")); } } 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) { Q_UNUSED(oldUrl); const bool block = m_urlNavigator->signalsBlocked(); m_urlNavigator->blockSignals(true); // Assure that the location state is reset for redirection URLs. This // allows to skip redirection URLs when going back or forward in the // URL history. m_urlNavigator->saveLocationState(QByteArray()); m_urlNavigator->setLocationUrl(newUrl); setSearchModeEnabled(isSearchUrl(newUrl)); m_urlNavigator->blockSignals(block); } void DolphinViewContainer::requestFocus() { m_view->setFocus(); } void DolphinViewContainer::saveUrlCompletionMode(KGlobalSettings::Completion completion) { DolphinSettings& settings = DolphinSettings::instance(); settings.generalSettings()->setUrlCompletionMode(completion); settings.save(); } void DolphinViewContainer::slotHistoryChanged() { QByteArray locationState = m_urlNavigator->locationState(); - if (!locationState.isEmpty()) { QDataStream stream(&locationState, QIODevice::ReadOnly); m_view->restoreState(stream); } } void DolphinViewContainer::startSearching(const QString &text) { Q_UNUSED(text); const KUrl url = m_searchBox->urlForSearching(); if (url.isValid() && !url.isEmpty()) { m_urlNavigator->setLocationUrl(url); } } void DolphinViewContainer::closeSearchBox() { setSearchModeEnabled(false); } void DolphinViewContainer::stopLoading() { m_view->stopLoading(); m_statusBar->setProgress(100); } bool DolphinViewContainer::isSearchUrl(const KUrl& url) const { const QString protocol = url.protocol(); 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:// when clicking on a tar file, - // zip:// 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 @@ -1,283 +1,295 @@ /*************************************************************************** * Copyright (C) 2007 by Peter Penz * * * * 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 DOLPHINVIEWCONTAINER_H #define DOLPHINVIEWCONTAINER_H #include #include #include #include #include #include #include #include class FilterBar; class KUrl; class KUrlNavigator; class DolphinSearchBox; 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 * @see FilterBar * @see KUrlNavigator * @see DolphinStatusBar */ class DolphinViewContainer : public QWidget { Q_OBJECT public: DolphinViewContainer(const KUrl& url, QWidget* parent); virtual ~DolphinViewContainer(); /** * Returns the current active URL, where all actions are applied. * The URL navigator is synchronized with this URL. */ KUrl url() const; /** * If \a active is true, the view container will marked as active. The active * view container is defined as view where all actions are applied to. */ void setActive(bool active); bool isActive() const; const DolphinStatusBar* statusBar() const; DolphinStatusBar* statusBar(); const KUrlNavigator* urlNavigator() const; KUrlNavigator* urlNavigator(); const DolphinView* view() const; DolphinView* view(); const DolphinSearchBox* searchBox() const; DolphinSearchBox* searchBox(); /** * Refreshes the view container to get synchronized with the (updated) Dolphin settings. */ void refresh(); /** Returns true, if the filter bar is visible. */ bool isFilterBarVisible() const; /** * Enables the search mode, if \p enabled is true. In the search mode the URL navigator * will be hidden and replaced by a line editor that allows to enter a search term. */ void setSearchModeEnabled(bool enabled); bool isSearchModeEnabled() const; public slots: /** * Sets the current active URL, where all actions are applied. The * URL navigator is synchronized with this URL. The signals * KUrlNavigator::urlChanged() and KUrlNavigator::historyChanged() * are emitted. * @see DolphinViewContainer::urlNavigator() */ void setUrl(const KUrl& url); /** * Popups the filter bar above the status bar if \a visible is true. * It \a visible is true, it is assured that the filter bar gains * the keyboard focus. */ void setFilterBarVisible(bool visible); signals: /** * Is emitted whenever the filter bar has changed its visibility state. */ void showFilterBarChanged(bool shown); /** * Is emitted when the write state of the folder has been changed. The application * should disable all actions like "Create New..." that depend on the write * state. */ void writeStateChanged(bool isFolderWritable); /** * Is emitted if the search mode has been enabled or disabled. * (see DolphinViewContainer::setSearchModeEnabled() and * DolphinViewContainer::isSearchModeEnabled()) */ void searchModeChanged(bool enabled); private slots: /** * Updates the number of items (= number of files + number of * directories) in the statusbar. If files are selected, the number * of selected files and the sum of the filesize is shown. The update * is done asynchronously, as getting the sum of the * filesizes can be an expensive operation. * Unless a previous OperationCompletedMessage was set very shortly before * calling this method, it will be overwritten (see DolphinStatusBar::setMessage). * Previous ErrorMessages however are always preserved. */ void delayedStatusBarUpdate(); /** * Is invoked by DolphinViewContainer::delayedStatusBarUpdate() and * updates the status bar synchronously. */ void updateStatusBar(); void updateProgress(int percent); /** * Updates the statusbar to show an undetermined progress with the correct * context information whether a searching or a directory loading is done. */ void slotStartedPathLoading(); /** * Assures that the viewport position is restored and updates the * statusbar to reflect the current content. */ void slotFinishedPathLoading(); /** * Handles clicking on an item. If the item is a directory, the * directory is opened in the view. If the item is a file, the file * gets started by the corresponding application. */ void slotItemTriggered(const KFileItem& item); /** * Opens a the file \a url by opening the corresponding application. * Is connected with the signal urlIsFile() from DolphinDirLister and will * get invoked if the user manually has entered a file into the URL navigator. */ void openFile(const KUrl& url); /** * Shows the information for the item \a item inside the statusbar. If the * item is null, the default statusbar information is shown. */ void showItemInfo(const KFileItem& item); /** Shows the information \a msg inside the statusbar. */ void showInfoMessage(const QString& msg); /** Shows the error message \a msg inside the statusbar. */ void showErrorMessage(const QString& msg); /** Shows the "operation completed" message \a msg inside the statusbar. */ void showOperationCompletedMessage(const QString& msg); void closeFilterBar(); /** * Filters the currently shown items by \a nameFilter. All items * which contain the given filter string will be shown. */ void setNameFilter(const QString& nameFilter); /** * Marks the view container as active * (see DolphinViewContainer::setActive()). */ 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 * that the root URL of the view is respected. */ void slotUrlNavigatorLocationChanged(const KUrl& url); /** * Is connected with the URL navigator and drops the URLs * above the destination \a destination. */ void dropUrls(const KUrl& destination, QDropEvent* event); /** * Is invoked when a redirection is done and changes the * URL of the URL navigator to \a newUrl without triggering * a reloading of the directory. */ void redirect(const KUrl& oldUrl, const KUrl& newUrl); /** Requests the focus for the view \a m_view. */ void requestFocus(); /** * Saves the currently used URL completion mode of * the URL navigator. */ void saveUrlCompletionMode(KGlobalSettings::Completion completion); void slotHistoryChanged(); /** * Gets the search URL from the searchbox and starts searching. * @param text Text the user has entered into the searchbox. */ void startSearching(const QString& text); void closeSearchBox(); /** * Stops the loading of a directory. Is connected with the "stopPressed" signal * from the statusbar. */ void stopLoading(); private: /** * @return True if the URL protocol is a search URL (e. g. nepomuksearch:// or filenamesearch://). */ 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; DolphinSearchBox* m_searchBox; DolphinView* m_view; FilterBar* m_filterBar; DolphinStatusBar* m_statusBar; QTimer* m_statusBarTimer; // Triggers a delayed update QElapsedTimer m_statusBarTimestamp; // Time in ms since last update }; #endif // DOLPHINVIEWCONTAINER_H 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 * + * * + * 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 +#include + +#include + +#include +#include + +#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()); + setGroupHeaderCreator(new KItemListGroupHeaderCreator()); + + 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 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 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 KFileItemListView::visibleRoleSizes() const +{ + QElapsedTimer timer; + timer.start(); + + QHash sizes; + + const int itemCount = model()->count(); + for (int i = 0; i < itemCount; ++i) { + QHashIterator 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(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(current)); + + if (m_modelRolesUpdater) { + delete m_modelRolesUpdater; + } + + m_modelRolesUpdater = new KFileItemModelRolesUpdater(static_cast(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& current, const QHash& previous) +{ + Q_UNUSED(previous); + + Q_ASSERT(qobject_cast(model())); + KFileItemModel* fileItemModel = static_cast(model()); + + // KFileItemModel does not distinct between "visible" and "invisible" roles. + // Add all roles that are mandatory for having a working KFileItemListView: + QSet keys = current.keys().toSet(); + QSet 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 * + * * + * 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 + +#include + +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 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& current, const QHash& 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 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 * + * * + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +//#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(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& current, + const QSet& roles) +{ + KItemListWidget::dataChanged(current, roles); + m_dirtyContent = true; + + QSet dirtyRoles; + if (roles.isEmpty()) { + dirtyRoles = visibleRoles().keys().toSet(); + dirtyRoles.insert("iconPixmap"); + dirtyRoles.insert("iconName"); + } else { + dirtyRoles = roles; + } + + QSetIterator it(dirtyRoles); + while (it.hasNext()) { + const QByteArray& role = it.next(); + m_dirtyContentRoles.insert(role); + } +} + +void KFileItemListWidget::visibleRolesChanged(const QHash& current, + const QHash& 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 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& current, + const QHash& 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 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 values = data(); + const QSizeF widgetSize = size(); + + int scaledIconHeight = 0; + if (iconOnTop) { + scaledIconHeight = static_cast(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(); + 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 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 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 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(); + text = i18ncp("@item:intable", "%1 item", "%1 items", size); + } + } else { + const KIO::filesize_t size = roleValue.value(); + 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 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 * + * * + * 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 + +#include + +#include +#include +#include + +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& current, const QSet& roles = QSet()); + virtual void visibleRolesChanged(const QHash& current, const QHash& previous); + virtual void visibleRolesSizesChanged(const QHash& current, const QHash& 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 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 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 * + * * + * 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 +#include +#include +#include + +#include + +#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 KFileItemModel::data(int index) const +{ + if (index >= 0 && index < count()) { + return m_data.at(index); + } + return QHash(); +} + +bool KFileItemModel::setData(int index, const QHash& values) +{ + if (index >= 0 && index < count()) { + QHash currentValue = m_data.at(index); + + QSet changedRoles; + QHashIterator 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& 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 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()); + } +} + +QSet KFileItemModel::roles() const +{ + QSet 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 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 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 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 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 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 + // Copyright (C) 2006 by Dominic Battre + // Copyright (C) 2006 by Martin Pool + + 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 * + * * + * 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 +#include +#include +#include + +#include + +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 data(int index) const; + virtual bool setData(int index, const QHash &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& roles); + QSet 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 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 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 m_items; // Allows O(1) access for KFileItemModel::index(const KFileItem& item) + QList > 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 * + * * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Required includes for subDirectoriesCount(): +#ifdef Q_WS_WIN + #include +#else + #include + #include +#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)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + + // 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& 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 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 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& 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 data = rolesData(item); + data.insert("iconPixmap", scaledPixmap); + + disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + m_model->setData(index, data); + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); +} + +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 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& pendingItems = (i == 0) ? m_pendingVisibleItems : m_pendingInvisibleItems; + QSetIterator 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 pendingItems; + pendingItems += m_pendingVisibleItems; + pendingItems += m_pendingInvisibleItems; + + resetPendingRoles(); + Q_ASSERT(m_pendingVisibleItems.isEmpty()); + Q_ASSERT(m_pendingInvisibleItems.isEmpty()); + + QSetIterator 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 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)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + m_model->setData(index, data); + connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + return true; + } + + return false; +} + +QHash KFileItemModelRolesUpdater::rolesData(const KFileItem& item) const +{ + QHash 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& items) const +{ + KFileItemList itemList; + if (items.isEmpty()) { + return itemList; + } + +#ifdef KFILEITEMMODELROLESUPDATER_DEBUG + QElapsedTimer timer; + timer.start(); +#endif + + QList indexes; + indexes.reserve(items.count()); + + QSetIterator 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& items, int count) +{ + KFileItemList itemList; + + int index = 0; + QSetIterator 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 + + 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 * + * * + * 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 + +#include +#include + +#include +#include +#include +#include + +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& roles); + QSet 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& 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 rolesData(const KFileItem& item) const; + + KFileItemList sortedItems(const QSet& items) const; + + static KFileItemList itemSubSet(const QSet& 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 m_roles; + QStringList m_enabledPlugins; + + QSet m_pendingVisibleItems; + QSet m_pendingInvisibleItems; + QList 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 * + * * + * 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 +#include +#include +#include + +#include + +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(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(viewport())->scene()->setSceneRect(0, 0, rect.width(), rect.height()); + static_cast(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/views/dolphindirlister.h b/src/kitemviews/kitemlistcontainer.h similarity index 51% copy from src/views/dolphindirlister.h copy to src/kitemviews/kitemlistcontainer.h index 662c7c0aa..83044c4f8 100644 --- a/src/views/dolphindirlister.h +++ b/src/kitemviews/kitemlistcontainer.h @@ -1,49 +1,70 @@ /*************************************************************************** - * Copyright (C) 2006-2009 by Peter Penz * + * Copyright (C) 2011 by Peter Penz * + * * + * 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 DOLPHINDIRLISTER_H -#define DOLPHINDIRLISTER_H +#ifndef KITEMLISTCONTAINER_H +#define KITEMLISTCONTAINER_H #include -#include + +#include + +class KItemListController; +class KItemListView; +class KItemModelBase; /** - * @brief Extends the class KDirLister by emitting a signal when an - * error occurred instead of showing an error dialog. + * @brief Provides a QWidget based scrolling view for a KItemListController. + * + * @see KItemListController */ -class LIBDOLPHINPRIVATE_EXPORT DolphinDirLister : public KDirLister +class LIBDOLPHINPRIVATE_EXPORT KItemListContainer : public QAbstractScrollArea { Q_OBJECT public: - DolphinDirLister(); - virtual ~DolphinDirLister(); + explicit KItemListContainer(KItemListController* controller, QWidget* parent = 0); + KItemListContainer(QWidget* parent = 0); + virtual ~KItemListContainer(); -signals: - /** Is emitted whenever an error has occurred. */ - void errorMessage(const QString& msg); - - /** Is emitted when the URL of the directory lister represents a file. */ - void urlIsFileError(const KUrl& url); + KItemListController* controller() const; protected: - virtual void handleError(KIO::Job* job); + 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 * + * * + * 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 +#include +#include + +#include + +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(event)); + case QEvent::KeyPress: + return keyPressEvent(static_cast(event)); + case QEvent::InputMethod: + return inputMethodEvent(static_cast(event)); + case QEvent::GraphicsSceneMousePress: + return mousePressEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneMouseMove: + return mouseMoveEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneMouseRelease: + return mouseReleaseEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneWheel: + return wheelEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneDragEnter: + return dragEnterEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneDragLeave: + return dragLeaveEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneDragMove: + return dragMoveEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneDrop: + return dropEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneHoverEnter: + return hoverEnterEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneHoverMove: + return hoverMoveEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneHoverLeave: + return hoverLeaveEvent(static_cast(event), QTransform()); + case QEvent::GraphicsSceneResize: + return resizeEvent(static_cast(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 * + * * + * 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 + +#include + +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/settings/viewmodes/columnviewsettingspage.h b/src/kitemviews/kitemlistgroupheader.cpp similarity index 55% rename from src/settings/viewmodes/columnviewsettingspage.h rename to src/kitemviews/kitemlistgroupheader.cpp index 2635e71f8..4db253293 100644 --- a/src/settings/viewmodes/columnviewsettingspage.h +++ b/src/kitemviews/kitemlistgroupheader.cpp @@ -1,66 +1,58 @@ /*************************************************************************** - * Copyright (C) 2006 by Peter Penz * + * Copyright (C) 2011 by Peter Penz * + * * + * 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 COLUMNVIEWSETTINGSPAGE_H -#define COLUMNVIEWSETTINGSPAGE_H - -#include "viewsettingspagebase.h" +#include "kitemlistgroupheader.h" -class DolphinFontRequester; -class IconSizeGroupBox; -class KComboBox; +#include "kitemlistview.h" -/** - * @brief Represents the page from the Dolphin Settings which allows - * to modify the settings for the details view. - */ -class ColumnViewSettingsPage : public ViewSettingsPageBase -{ - Q_OBJECT +#include -public: - ColumnViewSettingsPage(QWidget* parent); - virtual ~ColumnViewSettingsPage(); +#include - /** - * Applies the settings for the details view. - * The settings are persisted automatically when - * closing Dolphin. - */ - virtual void applySettings(); +KItemListGroupHeader::KItemListGroupHeader(QGraphicsWidget* parent) : + QGraphicsWidget(parent, 0) +{ +} - /** Restores the settings to default values. */ - virtual void restoreDefaults(); +KItemListGroupHeader::~KItemListGroupHeader() +{ +} -private: - void loadSettings(); +QSizeF KItemListGroupHeader::sizeHint(Qt::SizeHint which, const QSizeF& constraint) const +{ + Q_UNUSED(which); + Q_UNUSED(constraint); + return QSizeF(); +} -private: - enum - { - BaseTextWidth = 200, - TextInc = 50 - }; +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()); - IconSizeGroupBox* m_iconSizeGroupBox; - DolphinFontRequester* m_fontRequester; - KComboBox* m_textWidthBox; -}; + //painter->drawText(rect(), QString::number(m_index)); +} -#endif +#include "kitemlistgroupheader.moc" diff --git a/src/tests/dolphinviewtest_columns.cpp b/src/kitemviews/kitemlistgroupheader.h similarity index 66% copy from src/tests/dolphinviewtest_columns.cpp copy to src/kitemviews/kitemlistgroupheader.h index 40b88a223..135fd5e5f 100644 --- a/src/tests/dolphinviewtest_columns.cpp +++ b/src/kitemviews/kitemlistgroupheader.h @@ -1,42 +1,45 @@ /*************************************************************************** - * Copyright (C) 2010 by Frank Reininghaus (frank78ac@googlemail.com) * + * Copyright (C) 2011 by Peter Penz * * * * 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 +#ifndef KITEMLISTGROUPHEADER_H +#define KITEMLISTGROUPHEADER_H -#include "dolphinviewtest_allviewmodes.h" +#include -class DolphinViewTest_Columns : public DolphinViewTest_AllViewModes +#include + +class KItemListView; + +class LIBDOLPHINPRIVATE_EXPORT KItemListGroupHeader : public QGraphicsWidget { Q_OBJECT public: + KItemListGroupHeader(QGraphicsWidget* parent = 0); + virtual ~KItemListGroupHeader(); - virtual DolphinView::Mode mode() const { - return DolphinView::ColumnView; - } - - virtual bool verifyCorrectViewMode(const DolphinView* view) const { - return (view->mode() == DolphinView::ColumnView); - } + void setIndex(int index); + int index() const; + 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 -QTEST_KDEMAIN(DolphinViewTest_Columns, GUI) -#include "dolphinviewtest_columns.moc" 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 * + * * + * 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/views/dolphindirlister.h b/src/kitemviews/kitemlistselectionmanager.h similarity index 55% copy from src/views/dolphindirlister.h copy to src/kitemviews/kitemlistselectionmanager.h index 662c7c0aa..5c8e84614 100644 --- a/src/views/dolphindirlister.h +++ b/src/kitemviews/kitemlistselectionmanager.h @@ -1,49 +1,69 @@ /*************************************************************************** - * Copyright (C) 2006-2009 by Peter Penz * + * Copyright (C) 2011 by Peter Penz * + * * + * 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 DOLPHINDIRLISTER_H -#define DOLPHINDIRLISTER_H +#ifndef KITEMLISTSELECTIONMANAGER_H +#define KITEMLISTSELECTIONMANAGER_H #include -#include -/** - * @brief Extends the class KDirLister by emitting a signal when an - * error occurred instead of showing an error dialog. - */ -class LIBDOLPHINPRIVATE_EXPORT DolphinDirLister : public KDirLister +#include + +class KItemModelBase; + +class LIBDOLPHINPRIVATE_EXPORT KItemListSelectionManager : public QObject { Q_OBJECT public: - DolphinDirLister(); - virtual ~DolphinDirLister(); + enum SelectionMode { + Select, + Deselect, + Toggle + }; + + KItemListSelectionManager(QObject* parent = 0); + virtual ~KItemListSelectionManager(); -signals: - /** Is emitted whenever an error has occurred. */ - void errorMessage(const QString& msg); + void setCurrentItem(int current); + int currentItem() const; + + void setAnchorItem(int anchor); + int anchorItem() const; - /** Is emitted when the URL of the directory lister represents a file. */ - void urlIsFileError(const KUrl& url); + KItemModelBase* model() const; + +signals: + void currentChanged(int current, int previous); + void anchorChanged(int anchor, int previous); protected: - virtual void handleError(KIO::Job* job); + 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 * + * * + * 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 +#include + +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::iterator begin = m_sizeHintCache.begin() + index; + const QList::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& 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/views/dolphindirlister.h b/src/kitemviews/kitemlistsizehintresolver_p.h similarity index 64% copy from src/views/dolphindirlister.h copy to src/kitemviews/kitemlistsizehintresolver_p.h index 662c7c0aa..f4f65eccb 100644 --- a/src/views/dolphindirlister.h +++ b/src/kitemviews/kitemlistsizehintresolver_p.h @@ -1,49 +1,50 @@ /*************************************************************************** - * Copyright (C) 2006-2009 by Peter Penz * + * Copyright (C) 2011 by Peter Penz * * * * 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 DOLPHINDIRLISTER_H -#define DOLPHINDIRLISTER_H +#ifndef KITEMLISTSIZEHINTRESOLVER_H +#define KITEMLISTSIZEHINTRESOLVER_H #include -#include -/** - * @brief Extends the class KDirLister by emitting a signal when an - * error occurred instead of showing an error dialog. - */ -class LIBDOLPHINPRIVATE_EXPORT DolphinDirLister : public KDirLister -{ - Q_OBJECT +#include +#include +#include + +class KItemListView; +class LIBDOLPHINPRIVATE_EXPORT KItemListSizeHintResolver +{ public: - DolphinDirLister(); - virtual ~DolphinDirLister(); + KItemListSizeHintResolver(const KItemListView* itemListView); + virtual ~KItemListSizeHintResolver(); + QSizeF sizeHint(int index) const; -signals: - /** Is emitted whenever an error has occurred. */ - void errorMessage(const QString& msg); + 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& roles); - /** Is emitted when the URL of the directory lister represents a file. */ - void urlIsFileError(const KUrl& url); + void clearCache(); -protected: - virtual void handleError(KIO::Job* job); +private: + const KItemListView* m_itemListView; + mutable QList m_sizeHintCache; }; #endif diff --git a/src/tests/dolphinviewtest_details.cpp b/src/kitemviews/kitemliststyleoption.cpp similarity index 70% rename from src/tests/dolphinviewtest_details.cpp rename to src/kitemviews/kitemliststyleoption.cpp index 0034afd90..261dfc07b 100644 --- a/src/tests/dolphinviewtest_details.cpp +++ b/src/kitemviews/kitemliststyleoption.cpp @@ -1,42 +1,37 @@ /*************************************************************************** - * Copyright (C) 2010 by Frank Reininghaus (frank78ac@googlemail.com) * + * Copyright (C) 2011 by Peter Penz * * * * 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 +#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 similarity index 71% rename from src/tests/dolphinviewtest_columns.cpp rename to src/kitemviews/kitemliststyleoption.h index 40b88a223..d181204d7 100644 --- a/src/tests/dolphinviewtest_columns.cpp +++ b/src/kitemviews/kitemliststyleoption.h @@ -1,42 +1,41 @@ /*************************************************************************** - * Copyright (C) 2010 by Frank Reininghaus (frank78ac@googlemail.com) * + * Copyright (C) 2011 by Peter Penz * * * * 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 +#ifndef KITEMLISTSTYLEOPTION_H +#define KITEMLISTSTYLEOPTION_H -#include "dolphinviewtest_allviewmodes.h" +#include -class DolphinViewTest_Columns : public DolphinViewTest_AllViewModes -{ - Q_OBJECT +#include +#include +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 * + * * + * 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 + +#include +#include +#include +#include + +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& roles) +{ + const QHash previousRoles = m_visibleRoles; + m_visibleRoles = roles; + + QHashIterator 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 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 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 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 KItemListView::visibleRoleSizes() const +{ + return QHash(); +} + +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& current, const QHash& 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 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 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 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 itemsToMove; + QHashIterator 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& 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(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)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + 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)), + this, SLOT(slotItemsChanged(KItemRangeList,QSet))); + 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 reusableItems; + QHashIterator 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 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 * + * * + * 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 + +#include +#include +#include +#include +#include + +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& roles); + QHash 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: + * + * itemListView->setWidgetCreator(new KItemListWidgetCreator()); + * + * 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 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& current, const QHash& 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 visibleItemListWidgets() const; + +protected slots: + virtual void slotItemsInserted(const KItemRangeList& itemRanges); + virtual void slotItemsRemoved(const KItemRangeList& itemRanges); + virtual void slotItemsChanged(const KItemRangeList& itemRanges, + const QSet& 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 m_visibleRoles; + QHash m_visibleRolesSizes; + KItemListWidgetCreatorBase* m_widgetCreator; + KItemListGroupHeaderCreatorBase* m_groupHeaderCreator; + KItemListStyleOption m_styleOption; + + QHash m_visibleItems; + QHash 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 m_createdWidgets; + QList 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 LIBDOLPHINPRIVATE_EXPORT KItemListWidgetCreator : public KItemListWidgetCreatorBase +{ +public: + virtual ~KItemListWidgetCreator(); + virtual KItemListWidget* create(KItemListView* view); +}; + +template +KItemListWidgetCreator::~KItemListWidgetCreator() +{ +} + +template +KItemListWidget* KItemListWidgetCreator::create(KItemListView* view) +{ + KItemListWidget* widget = static_cast(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 LIBDOLPHINPRIVATE_EXPORT KItemListGroupHeaderCreator : public KItemListGroupHeaderCreatorBase +{ +public: + virtual ~KItemListGroupHeaderCreator(); + virtual KItemListGroupHeader* create(QGraphicsWidget* parent); +}; + +template +KItemListGroupHeaderCreator::~KItemListGroupHeaderCreator() +{ +} + +template +KItemListGroupHeader* KItemListGroupHeaderCreator::create(QGraphicsWidget* parent) +{ + KItemListGroupHeader* widget = static_cast(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 * + * * + * 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 + +#include +#include + +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 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(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(type))) { + return true; + } + } + return false; +} + +void KItemListViewAnimation::slotFinished() +{ + QPropertyAnimation* finishedAnim = qobject_cast(sender()); + for (int type = 0; type < AnimationTypeCount; ++type) { + QHashIterator 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(type)); + return; + } + } + } + Q_ASSERT(false); +} + +#include "kitemlistviewanimation_p.moc" diff --git a/src/views/dolphindirlister.h b/src/kitemviews/kitemlistviewanimation_p.h similarity index 50% copy from src/views/dolphindirlister.h copy to src/kitemviews/kitemlistviewanimation_p.h index 662c7c0aa..0bf54d296 100644 --- a/src/views/dolphindirlister.h +++ b/src/kitemviews/kitemlistviewanimation_p.h @@ -1,49 +1,79 @@ /*************************************************************************** - * Copyright (C) 2006-2009 by Peter Penz * + * Copyright (C) 2011 by Peter Penz * * * * 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 DOLPHINDIRLISTER_H -#define DOLPHINDIRLISTER_H +#ifndef KITEMLISTVIEWANIMATION_H +#define KITEMLISTVIEWANIMATION_H #include -#include -/** - * @brief Extends the class KDirLister by emitting a signal when an - * error occurred instead of showing an error dialog. - */ -class LIBDOLPHINPRIVATE_EXPORT DolphinDirLister : public KDirLister +#include +#include +#include + +class KItemListView; +class QGraphicsWidget; +class QPointF; +class QPropertyAnimation; + +class LIBDOLPHINPRIVATE_EXPORT KItemListViewAnimation : public QObject { Q_OBJECT public: - DolphinDirLister(); - virtual ~DolphinDirLister(); + 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: - /** Is emitted whenever an error has occurred. */ - void errorMessage(const QString& msg); + void finished(QGraphicsWidget* widget, KItemListViewAnimation::AnimationType type); + +private slots: + void slotFinished(); - /** Is emitted when the URL of the directory lister represents a file. */ - void urlIsFileError(const KUrl& url); +private: + enum { AnimationTypeCount = 4 }; -protected: - virtual void handleError(KIO::Job* job); + Qt::Orientation m_scrollOrientation; + qreal m_offset; + QHash 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 * + * * + * 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 + +#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(this)->doLayout(); + return m_maximumOffset; +} + +int KItemListViewLayouter::firstVisibleIndex() const +{ + const_cast(this)->doLayout(); + return m_firstVisibleIndex; +} + +int KItemListViewLayouter::lastVisibleIndex() const +{ + const_cast(this)->doLayout(); + return m_lastVisibleIndex; +} + +QRectF KItemListViewLayouter::itemBoundingRect(int index) const +{ + const_cast(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(this)->doLayout(); + + const int height = static_cast(m_size.height()); + const int rowHeight = static_cast(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(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 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 * + * * + * 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 + +#include +#include +#include +#include + +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 m_groups; + + // Stores all item indexes that are the first item of a group. + // Assures fast access for KItemListViewLayouter::isFirstGroupItem(). + QSet m_groupIndexes; + + QList 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 * + * * + * 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 + +#include +#include +#include + +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& data, + const QSet& 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 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& roles) +{ + const QHash previousRoles = m_visibleRoles; + m_visibleRoles = roles; + visibleRolesChanged(roles, previousRoles); +} + +QHash KItemListWidget::visibleRoles() const +{ + return m_visibleRoles; +} + +void KItemListWidget::setVisibleRolesSizes(const QHash rolesSizes) +{ + const QHash previousRolesSizes = m_visibleRolesSizes; + m_visibleRolesSizes = rolesSizes; + visibleRolesSizesChanged(rolesSizes, previousRolesSizes); +} + +QHash 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& current, + const QSet& roles) +{ + Q_UNUSED(current); + Q_UNUSED(roles); + update(); +} + +void KItemListWidget::visibleRolesChanged(const QHash& current, + const QHash& previous) +{ + Q_UNUSED(current); + Q_UNUSED(previous); + update(); +} + +void KItemListWidget::visibleRolesSizesChanged(const QHash& current, + const QHash& 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 * + * * + * 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 + +#include + +#include + +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& data, const QSet& roles = QSet()); + QHash 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& roles); + QHash visibleRoles() const; + + void setVisibleRolesSizes(const QHash rolesSizes); + QHash 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& current, const QSet& roles = QSet()); + virtual void visibleRolesChanged(const QHash& current, const QHash& previous); + virtual void visibleRolesSizesChanged(const QHash& current, const QHash& 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 m_data; + QHash m_visibleRoles; + QHash 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 * + * * + * 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 &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 * + * * + * 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 + +#include +#include +#include +#include + +struct KItemRange +{ + KItemRange(int index, int count); + int index; + int count; +}; +typedef QList 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 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& 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& 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 + Copyright © 2006-2007, 2008 Fredrik Höglund + + The stack blur algorithm was invented by Mario Klingemann + + 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 +#include +#include +#include + +#include + +#include // for HAVE_XRENDER +#if defined(Q_WS_X11) && defined(HAVE_XRENDER) +# include +# include +# include +#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(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(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 similarity index 71% rename from src/tests/dolphinviewtest_icons.cpp rename to src/kitemviews/kpixmapmodifier_p.h index 5f928f403..bca5e442a 100644 --- a/src/tests/dolphinviewtest_icons.cpp +++ b/src/kitemviews/kpixmapmodifier_p.h @@ -1,42 +1,37 @@ /*************************************************************************** - * Copyright (C) 2010 by Frank Reininghaus (frank78ac@googlemail.com) * + * Copyright (C) 2011 by Peter Penz * * * * 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 +#ifndef KPIXMAPMODIFIER_H +#define KPIXMAPMODIFIER_H -#include "dolphinviewtest_allviewmodes.h" +#include -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 @@ -1,88 +1,88 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * Copyright (C) 2006 by Stefan Monov * * * * 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 "dolphinapplication.h" #include "dolphinmainwindow.h" #include #include #include #include #include extern "C" 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")); about.setHomepage("http://dolphin.kde.org"); about.addAuthor(ki18nc("@info:credit", "Peter Penz"), ki18nc("@info:credit", "Maintainer and developer"), "peter.penz19@gmail.com"); about.addAuthor(ki18nc("@info:credit", "David Faure"), ki18nc("@info:credit", "Developer"), "faure@kde.org"); about.addAuthor(ki18nc("@info:credit", "Aaron J. Seigo"), ki18nc("@info:credit", "Developer"), "aseigo@kde.org"); about.addAuthor(ki18nc("@info:credit", "Rafael Fernández López"), ki18nc("@info:credit", "Developer"), "ereslibre@kde.org"); about.addAuthor(ki18nc("@info:credit", "Kevin Ottens"), ki18nc("@info:credit", "Developer"), "ervin@kde.org"); about.addAuthor(ki18nc("@info:credit", "Holger Freyther"), ki18nc("@info:credit", "Developer"), "freyther@gmx.net"); about.addAuthor(ki18nc("@info:credit", "Max Blazejak"), ki18nc("@info:credit", "Developer"), "m43ksrocks@gmail.com"); about.addAuthor(ki18nc("@info:credit", "Michael Austin"), ki18nc("@info:credit", "Documentation"), "tuxedup@users.sourceforge.net"); // the .desktop file is not taken into account when launching manually, so // set the icon precautionally: about.setProgramIconName("system-file-manager"); KCmdLineArgs::init(argc, argv, &about); KCmdLineOptions options; options.add("select", ki18nc("@info:shell", "The files and directories passed as arguments " "will be selected.")); options.add("split", ki18nc("@info:shell", "Dolphin will get started with a split view.")); options.add("+[Url]", ki18nc("@info:shell", "Document to open")); KCmdLineArgs::addCmdLineOptions(options); DolphinApplication app; KGlobal::locale()->insertCatalog("libkonq"); // needed for applications using libkonq if (app.isSessionRestored()) { app.restoreSession(); } app.exec(); // krazy:exclude=crashy return 0; } 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 @@ -1,18 +1,18 @@ - - + + false true 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 @@ -1,320 +1,321 @@ /*************************************************************************** * Copyright (C) 2006-2010 by Peter Penz * * * * 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 "folderspanel.h" #include "settings/dolphinsettings.h" #include "dolphin_folderspanelsettings.h" #include "dolphin_generalsettings.h" #include "paneltreeview.h" #include "treeviewcontextmenu.h" #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include -#include #include #include #include FoldersPanel::FoldersPanel(QWidget* parent) : Panel(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() { setLayoutDirection(Qt::LeftToRight); } 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) { m_treeView->setAutoHorizontalScroll(enable); FoldersPanelSettings::setAutoScrolling(enable); } bool FoldersPanel::autoScrolling() const { return FoldersPanelSettings::autoScrolling(); } 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); dialog->show(); dialog->raise(); dialog->activateWindow(); } } bool FoldersPanel::urlChanged() { if (!url().isValid() || url().protocol().contains("search")) { // Skip results shown by a search, as possible identical // directory names are useless without parent-path information. return false; } if (m_dirLister) { m_setLeafVisible = true; loadTree(url()); } return true; } void FoldersPanel::showEvent(QShowEvent* event) { if (event->spontaneous()) { Panel::showEvent(event); return; } if (!m_dirLister) { // Postpone the creating of the dir lister to the first show event. // This assures that no performance and memory overhead is given when the TreeView is not // used at all (see FoldersPanel::setUrl()). m_dirLister = new KDirLister(); m_dirLister->setDirOnlyMode(true); m_dirLister->setAutoUpdate(true); 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); connect(m_dolphinModel, SIGNAL(expand(const QModelIndex&)), this, SLOT(expandToDir(const QModelIndex&))); Q_ASSERT(!m_proxyModel); m_proxyModel = new DolphinSortFilterProxyModel(this); m_proxyModel->setSourceModel(m_dolphinModel); Q_ASSERT(!m_treeView); m_treeView = new PanelTreeView(this); m_treeView->setModel(m_proxyModel); m_proxyModel->setSorting(DolphinView::SortByName); m_proxyModel->setSortOrder(Qt::AscendingOrder); m_treeView->setAutoHorizontalScroll(FoldersPanelSettings::autoScrolling()); new FolderExpander(m_treeView, m_proxyModel); connect(m_treeView, SIGNAL(clicked(const QModelIndex&)), this, SLOT(updateActiveView(const QModelIndex&))); connect(m_treeView, SIGNAL(urlsDropped(const QModelIndex&, QDropEvent*)), this, SLOT(dropUrls(const QModelIndex&, QDropEvent*))); connect(m_treeView, SIGNAL(pressed(const QModelIndex&)), this, SLOT(updateMouseButtons())); connect(m_treeView->horizontalScrollBar(), SIGNAL(sliderMoved(int)), this, SLOT(slotHorizontalScrollBarMoved(int))); connect(m_treeView->verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotVerticalScrollBarMoved(int))); QVBoxLayout* layout = new QVBoxLayout(this); layout->setMargin(0); - layout->addWidget(m_treeView); + layout->addWidget(m_treeView);*/ } loadTree(url()); Panel::showEvent(event); } 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 contextMenu = new TreeViewContextMenu(this, item); contextMenu->open(); delete contextMenu; } void FoldersPanel::keyPressEvent(QKeyEvent* event) { const int key = event->key(); if ((key == Qt::Key_Enter) || (key == Qt::Key_Return)) { event->accept(); updateActiveView(m_treeView->currentIndex()); } else { Panel::keyPressEvent(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); + }*/ } } void FoldersPanel::expandToDir(const QModelIndex& index) { m_treeView->setExpanded(index, true); selectLeafDirectory(); } 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() { m_mouseButtons = QApplication::mouseButtons(); } void FoldersPanel::slotDirListerCompleted() { - m_treeView->resizeColumnToContents(DolphinModel::Name); +// m_treeView->resizeColumnToContents(DolphinModel::Name); } void FoldersPanel::slotHorizontalScrollBarMoved(int value) { Q_UNUSED(value); // Disable the auto-scrolling until the vertical scrollbar has // been moved by the user. m_treeView->setAutoHorizontalScroll(false); } void FoldersPanel::slotVerticalScrollBarMoved(int value) { Q_UNUSED(value); // Enable the auto-scrolling again (it might have been disabled by // moving the horizontal scrollbar). m_treeView->setAutoHorizontalScroll(FoldersPanelSettings::autoScrolling()); } void FoldersPanel::loadTree(const KUrl& url) { Q_ASSERT(m_dirLister); m_leafDir = url; KUrl baseUrl; if (url.isLocalFile()) { // use the root directory as base for local URLs (#150941) baseUrl = QDir::rootPath(); } else { // clear the path for non-local URLs and use it as base baseUrl = url; baseUrl.setPath(QString('/')); } if (m_dirLister->url() != baseUrl) { 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()) { QItemSelectionModel* selModel = m_treeView->selectionModel(); selModel->setCurrentIndex(proxyIndex, QItemSelectionModel::ClearAndSelect); if (m_setLeafVisible) { // Invoke scrollToLeaf() asynchronously. This assures that // the horizontal scrollbar is shown after resizing the column // (otherwise the scrollbar might hide the leaf). 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 @@ -1,132 +1,132 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * * * 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 FOLDERSPANEL_H #define FOLDERSPANEL_H #include #include class KDirLister; class DolphinModel; class DolphinSortFilterProxyModel; class PanelTreeView; class QModelIndex; /** * @brief Shows a tree view of the directories starting from * the currently selected place. * * The tree view is always synchronized with the currently active view * from the main window. */ class FoldersPanel : public Panel { Q_OBJECT 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; void rename(const KFileItem& item); signals: /** * Is emitted if the an URL change is requested. */ void changeUrl(const KUrl& url, Qt::MouseButtons buttons); protected: /** @see Panel::urlChanged() */ virtual bool urlChanged(); /** @see QWidget::showEvent() */ virtual void showEvent(QShowEvent* event); /** @see QWidget::contextMenuEvent() */ virtual void contextMenuEvent(QContextMenuEvent* event); /** @see QWidget::keyPressEvent() */ virtual void keyPressEvent(QKeyEvent* event); private slots: /** * Updates the active view to the URL * which is given by the item with the index \a index. */ void updateActiveView(const QModelIndex& index); /** * Is emitted if URLs have been dropped * to the index \a index. */ void dropUrls(const QModelIndex& index, QDropEvent* event); /** * Expands the treeview to show the directory * specified by \a index. */ void expandToDir(const QModelIndex& index); /** * Assures that the leaf folder gets visible. */ void scrollToLeaf(); void updateMouseButtons(); void slotDirListerCompleted(); void slotHorizontalScrollBarMoved(int value); void slotVerticalScrollBarMoved(int value); private: /** * Initializes the base URL of the tree and expands all * directories until \a url. * @param url URL of the leaf directory that should get expanded. */ void loadTree(const KUrl& url); /** * Selects the current leaf directory m_leafDir and assures * that the directory is visible if the leaf has been set by * FoldersPanel::setUrl(). */ void selectLeafDirectory(); 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; }; #endif // FOLDERSPANEL_H 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 @@ -1,142 +1,140 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * * * 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 "paneltreeview.h" #include #include #include #include #include #include -#include -#include - PanelTreeView::PanelTreeView(QWidget* parent) : KTreeView(parent) { setAcceptDrops(true); setUniformRowHeights(true); setSelectionMode(QAbstractItemView::SingleSelection); setEditTriggers(QAbstractItemView::NoEditTriggers); setSortingEnabled(true); setFrameStyle(QFrame::NoFrame); setDragDropMode(QAbstractItemView::DragDrop); setDropIndicatorShown(false); setVerticalScrollMode(QListView::ScrollPerPixel); setHorizontalScrollMode(QListView::ScrollPerPixel); viewport()->setAttribute(Qt::WA_Hover); // make the background transparent and apply the window-text color // to the text color, so that enough contrast is given for all color // schemes QPalette p = palette(); p.setColor(QPalette::Active, QPalette::Text, p.color(QPalette::Active, QPalette::WindowText)); p.setColor(QPalette::Inactive, QPalette::Text, p.color(QPalette::Inactive, QPalette::WindowText)); p.setColor(QPalette::Disabled, QPalette::Text, p.color(QPalette::Disabled, QPalette::WindowText)); setPalette(p); viewport()->setAutoFillBackground(false); KFileItemDelegate* delegate = new KFileItemDelegate(this); setItemDelegate(delegate); } PanelTreeView::~PanelTreeView() { } 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: // TODO: The opening/closing animation of subtrees flickers in combination with the // panel when using the Oxygen style. As workaround the animation is turned off: setAnimated(false); break; case QEvent::UpdateRequest: // a wheel movement will scroll 1 item if (model()->rowCount() > 0) { verticalScrollBar()->setSingleStep(sizeHintForRow(0) / 3); } break; default: break; } return KTreeView::event(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) { KTreeView::dragEnterEvent(event); if (event->mimeData()->hasUrls()) { event->acceptProposedAction(); } } void PanelTreeView::dragLeaveEvent(QDragLeaveEvent* event) { KTreeView::dragLeaveEvent(event); setDirtyRegion(m_dropRect); } void PanelTreeView::dragMoveEvent(QDragMoveEvent* event) { KTreeView::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 = visualRect(index); setDirtyRegion(m_dropRect); if (event->mimeData()->hasUrls()) { // accept url drops, independently from the destination item event->acceptProposedAction(); } } void PanelTreeView::dropEvent(QDropEvent* event) { const QModelIndex index = indexAt(event->pos()); if (index.isValid()) { emit urlsDropped(index, event); } KTreeView::dropEvent(event); } #include "paneltreeview.moc" 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 @@ -1,210 +1,210 @@ /*************************************************************************** * Copyright (C) 2006-2010 by Peter Penz * * Copyright (C) 2006 by Cvetoslav Ludmiloff * * * * 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 "treeviewcontextmenu.h" #include #include #include #include #include #include #include #include #include #include "folderspanel.h" #include #include TreeViewContextMenu::TreeViewContextMenu(FoldersPanel* parent, const KFileItem& fileInfo) : QObject(parent), m_parent(parent), m_fileInfo(fileInfo) { } TreeViewContextMenu::~TreeViewContextMenu() { } void TreeViewContextMenu::open() { KMenu* popup = new KMenu(m_parent); if (!m_fileInfo.isNull()) { KFileItemListProperties capabilities(KFileItemList() << m_fileInfo); // insert 'Cut', 'Copy' and 'Paste' QAction* cutAction = new QAction(KIcon("edit-cut"), i18nc("@action:inmenu", "Cut"), this); cutAction->setEnabled(capabilities.supportsMoving()); connect(cutAction, SIGNAL(triggered()), this, SLOT(cut())); QAction* copyAction = new QAction(KIcon("edit-copy"), i18nc("@action:inmenu", "Copy"), this); connect(copyAction, SIGNAL(triggered()), this, SLOT(copy())); QAction* pasteAction = new QAction(KIcon("edit-paste"), i18nc("@action:inmenu", "Paste"), this); const QMimeData* mimeData = QApplication::clipboard()->mimeData(); const KUrl::List pasteData = KUrl::List::fromMimeData(mimeData); connect(pasteAction, SIGNAL(triggered()), this, SLOT(paste())); pasteAction->setEnabled(!pasteData.isEmpty() && capabilities.supportsWriting()); popup->addAction(cutAction); popup->addAction(copyAction); popup->addAction(pasteAction); popup->addSeparator(); // insert 'Rename' QAction* renameAction = new QAction(i18nc("@action:inmenu", "Rename..."), this); renameAction->setEnabled(capabilities.supportsMoving()); renameAction->setIcon(KIcon("edit-rename")); connect(renameAction, SIGNAL(triggered()), this, SLOT(rename())); popup->addAction(renameAction); // insert 'Move to Trash' and (optionally) 'Delete' KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig("kdeglobals", KConfig::IncludeGlobals); KConfigGroup configGroup(globalConfig, "KDE"); bool showDeleteCommand = configGroup.readEntry("ShowDeleteCommand", false); const KUrl url = m_fileInfo.url(); if (url.isLocalFile()) { QAction* moveToTrashAction = new QAction(KIcon("user-trash"), i18nc("@action:inmenu", "Move to Trash"), this); const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving(); moveToTrashAction->setEnabled(enableMoveToTrash); connect(moveToTrashAction, SIGNAL(triggered()), this, SLOT(moveToTrash())); popup->addAction(moveToTrashAction); } else { showDeleteCommand = true; } if (showDeleteCommand) { QAction* deleteAction = new QAction(KIcon("edit-delete"), i18nc("@action:inmenu", "Delete"), this); deleteAction->setEnabled(capabilities.supportsDeleting()); connect(deleteAction, SIGNAL(triggered()), this, SLOT(deleteItem())); popup->addAction(deleteAction); } popup->addSeparator(); // insert 'Properties' entry QAction* propertiesAction = new QAction(i18nc("@action:inmenu", "Properties"), this); propertiesAction->setIcon(KIcon("document-properties")); connect(propertiesAction, SIGNAL(triggered()), this, SLOT(showProperties())); popup->addAction(propertiesAction); popup->addSeparator(); } 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))); QAction* autoScrollingAction = new QAction(i18nc("@action:inmenu", "Automatic Scrolling"), this); autoScrollingAction->setCheckable(true); autoScrollingAction->setChecked(m_parent->autoScrolling()); popup->addAction(autoScrollingAction); connect(autoScrollingAction, SIGNAL(toggled(bool)), this, SLOT(setAutoScrolling(bool))); popup->addSeparator(); foreach (QAction* action, m_parent->customContextMenuActions()) { popup->addAction(action); } popup->exec(QCursor::pos()); popup->deleteLater(); } void TreeViewContextMenu::populateMimeData(QMimeData* mimeData, bool cut) { KUrl::List kdeUrls; kdeUrls.append(m_fileInfo.url()); KUrl::List mostLocalUrls; bool dummy; mostLocalUrls.append(m_fileInfo.mostLocalUrl(dummy)); KonqMimeData::populateMimeData(mimeData, kdeUrls, mostLocalUrls, cut); } void TreeViewContextMenu::cut() { QMimeData* mimeData = new QMimeData(); populateMimeData(mimeData, true); QApplication::clipboard()->setMimeData(mimeData); } void TreeViewContextMenu::copy() { QMimeData* mimeData = new QMimeData(); populateMimeData(mimeData, false); QApplication::clipboard()->setMimeData(mimeData); } void TreeViewContextMenu::paste() { QClipboard* clipboard = QApplication::clipboard(); const QMimeData* mimeData = clipboard->mimeData(); const KUrl::List source = KUrl::List::fromMimeData(mimeData); const KUrl& dest = m_fileInfo.url(); if (KonqMimeData::decodeIsCutSelection(mimeData)) { KonqOperations::copy(m_parent, KonqOperations::MOVE, source, dest); clipboard->clear(); } else { KonqOperations::copy(m_parent, KonqOperations::COPY, source, dest); } } void TreeViewContextMenu::rename() { m_parent->rename(m_fileInfo); } void TreeViewContextMenu::moveToTrash() { KonqOperations::del(m_parent, KonqOperations::TRASH, m_fileInfo.url()); } void TreeViewContextMenu::deleteItem() { KonqOperations::del(m_parent, KonqOperations::DEL, m_fileInfo.url()); } void TreeViewContextMenu::showProperties() { KPropertiesDialog* dialog = new KPropertiesDialog(m_fileInfo.url(), m_parent); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); } void TreeViewContextMenu::setShowHiddenFiles(bool show) { - m_parent->setShowHiddenFiles(show); + m_parent->setHiddenFilesShown(show); } void TreeViewContextMenu::setAutoScrolling(bool enable) { m_parent->setAutoScrolling(enable); } #include "treeviewcontextmenu.moc" 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 @@ -1,14 +1,14 @@ - - + + true 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 @@ -1,427 +1,427 @@ /*************************************************************************** * Copyright (C) 2009 by Peter Penz * * * * 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 "informationpanelcontent.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dolphin_informationpanelsettings.h" #include "filemetadataconfigurationdialog.h" #include "settings/dolphinsettings.h" #include "phononwidget.h" #include "pixmapviewer.h" InformationPanelContent::InformationPanelContent(QWidget* parent) : QWidget(parent), m_item(), m_pendingPreview(false), m_outdatedPreviewTimer(0), m_preview(0), m_phononWidget(0), m_nameLabel(0), m_metaDataWidget(0), m_metaDataArea(0) { parent->installEventFilter(this); // Initialize timer for disabling an outdated preview with a small // delay. This prevents flickering if the new preview can be generated // within a very small timeframe. m_outdatedPreviewTimer = new QTimer(this); m_outdatedPreviewTimer->setInterval(300); m_outdatedPreviewTimer->setSingleShot(true); connect(m_outdatedPreviewTimer, SIGNAL(timeout()), this, SLOT(markOutdatedPreview())); QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(KDialog::spacingHint()); // preview const int minPreviewWidth = KIconLoader::SizeEnormous + KIconLoader::SizeMedium; m_preview = new PixmapViewer(parent); m_preview->setMinimumWidth(minPreviewWidth); m_preview->setMinimumHeight(KIconLoader::SizeEnormous); m_phononWidget = new PhononWidget(parent); m_phononWidget->hide(); m_phononWidget->setMinimumWidth(minPreviewWidth); connect(m_phononWidget, SIGNAL(playingStarted()), this, SLOT(slotPlayingStarted())); connect(m_phononWidget, SIGNAL(playingStopped()), this, SLOT(slotPlayingStopped())); // name m_nameLabel = new QLabel(parent); QFont font = m_nameLabel->font(); font.setBold(true); m_nameLabel->setFont(font); 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()); m_metaDataWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); connect(m_metaDataWidget, SIGNAL(urlActivated(KUrl)), this, SIGNAL(urlActivated(KUrl))); // Encapsulate the MetaDataWidget inside a container that has a dummy widget // at the bottom. This prevents that the meta data widget gets vertically stretched // in the case where the height of m_metaDataArea > m_metaDataWidget. QWidget* metaDataWidgetContainer = new QWidget(parent); QVBoxLayout* containerLayout = new QVBoxLayout(metaDataWidgetContainer); containerLayout->setContentsMargins(0, 0, 0, 0); containerLayout->setSpacing(0); containerLayout->addWidget(m_metaDataWidget); containerLayout->addStretch(); m_metaDataArea = new QScrollArea(parent); m_metaDataArea->setWidget(metaDataWidgetContainer); m_metaDataArea->setWidgetResizable(true); m_metaDataArea->setFrameShape(QFrame::NoFrame); QWidget* viewport = m_metaDataArea->viewport(); viewport->installEventFilter(this); QPalette palette = viewport->palette(); palette.setColor(viewport->backgroundRole(), QColor(Qt::transparent)); viewport->setPalette(palette); layout->addWidget(m_preview); layout->addWidget(m_phononWidget); layout->addWidget(m_nameLabel); layout->addWidget(new KSeparator()); layout->addWidget(m_metaDataArea); } InformationPanelContent::~InformationPanelContent() { InformationPanelSettings::self()->writeConfig(); } void InformationPanelContent::showItem(const KFileItem& item) { m_pendingPreview = false; const KUrl itemUrl = item.url(); const bool isSearchUrl = itemUrl.protocol().contains("search") && item.nepomukUri().isEmpty(); if (!applyPlace(itemUrl)) { setNameLabelText(item.text()); if (isSearchUrl) { // in the case of a search-URL the URL is not readable for humans // (at least not useful to show in the Information Panel) KIconLoader iconLoader; QPixmap icon = iconLoader.loadIcon("nepomuk", KIconLoader::NoGroup, KIconLoader::SizeEnormous); m_preview->setPixmap(icon); } else { // try to get a preview pixmap from the item... m_pendingPreview = true; // Mark the currently shown preview as outdated. This is done // with a small delay to prevent a flickering when the next preview // can be shown within a short timeframe. This timer is not started // for directories, as directory previews might fail and return the // same icon. if (!item.isDir()) { m_outdatedPreviewTimer->start(); } KIO::PreviewJob* job = KIO::filePreview(KFileItemList() << item, QSize(m_preview->width(), m_preview->height())); job->setScaleType(KIO::PreviewJob::Unscaled); connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)), this, SLOT(showPreview(const KFileItem&, const QPixmap&))); connect(job, SIGNAL(failed(const KFileItem&)), this, SLOT(showIcon(const KFileItem&))); } } if (m_metaDataWidget) { m_metaDataWidget->show(); 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 // thinks it supports PNG images if (usePhonon) { m_phononWidget->show(); PhononWidget::Mode mode = mimeType.startsWith(QLatin1String("video")) ? PhononWidget::Video : PhononWidget::Audio; m_phononWidget->setMode(mode); m_phononWidget->setUrl(item.targetUrl()); if ((mode == PhononWidget::Video) && m_preview->isVisible()) { m_phononWidget->setVideoSize(m_preview->size()); } } else { m_phononWidget->hide(); m_preview->setVisible(true); } } else { m_phononWidget->hide(); } m_item = item; } void InformationPanelContent::showItems(const KFileItemList& items) { m_pendingPreview = false; KIconLoader iconLoader; QPixmap icon = iconLoader.loadIcon("dialog-information", KIconLoader::NoGroup, KIconLoader::SizeEnormous); m_preview->setPixmap(icon); setNameLabelText(i18ncp("@info", "%1 item selected", "%1 items selected", items.count())); if (m_metaDataWidget) { m_metaDataWidget->setItems(items); } m_phononWidget->hide(); m_item = KFileItem(); } bool InformationPanelContent::eventFilter(QObject* obj, QEvent* event) { switch (event->type()) { case QEvent::Resize: { QResizeEvent* resizeEvent = static_cast(event); if (obj == m_metaDataArea->viewport()) { // The size of the meta text area has changed. Adjust the fixed // width in a way that no horizontal scrollbar needs to be shown. m_metaDataWidget->setFixedWidth(resizeEvent->size().width()); } else if (obj == parent()) { adjustWidgetSizes(resizeEvent->size().width()); } break; } case QEvent::Polish: adjustWidgetSizes(parentWidget()->width()); break; default: break; } return QWidget::eventFilter(obj, event); } void InformationPanelContent::configureSettings(const QList& customContextMenuActions) { KMenu popup(this); 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")); popup.addSeparator(); foreach (QAction* action, customContextMenuActions) { popup.addAction(action); } // Open the popup and adjust the settings for the // selected action. QAction* action = popup.exec(QCursor::pos()); if (!action) { return; } 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", "Select which data should be shown in the information panel:")); dialog->setItems(m_metaDataWidget->items()); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); dialog->raise(); dialog->activateWindow(); connect(dialog, SIGNAL(destroyed()), this, SLOT(refreshMetaData())); } } void InformationPanelContent::showIcon(const KFileItem& item) { m_outdatedPreviewTimer->stop(); m_pendingPreview = false; if (!applyPlace(item.targetUrl())) { KIcon icon(item.iconName(), KIconLoader::global(), item.overlays()); m_preview->setPixmap(icon.pixmap(KIconLoader::SizeEnormous)); } } void InformationPanelContent::showPreview(const KFileItem& item, const QPixmap& pixmap) { m_outdatedPreviewTimer->stop(); Q_UNUSED(item); if (m_pendingPreview) { QPixmap p = pixmap; KIconLoader::global()->drawOverlays(item.overlays(), p, KIconLoader::Desktop); m_preview->setPixmap(p); m_pendingPreview = false; } } void InformationPanelContent::markOutdatedPreview() { KIconEffect *iconEffect = KIconLoader::global()->iconEffect(); QPixmap disabledPixmap = iconEffect->apply(m_preview->pixmap(), KIconLoader::Desktop, KIconLoader::DisabledState); m_preview->setPixmap(disabledPixmap); } void InformationPanelContent::slotPlayingStarted() { m_preview->setVisible(m_phononWidget->mode() != PhononWidget::Video); } void InformationPanelContent::slotPlayingStopped() { m_preview->setVisible(true); } void InformationPanelContent::refreshMetaData() { if (!m_item.isNull() && m_item.nepomukUri().isValid()) { showItem(m_item); } } bool InformationPanelContent::applyPlace(const KUrl& url) { KFilePlacesModel* placesModel = DolphinSettings::instance().placesModel(); int count = placesModel->rowCount(); for (int i = 0; i < count; ++i) { QModelIndex index = placesModel->index(i, 0); if (url.equals(placesModel->url(index), KUrl::CompareWithoutTrailingSlash)) { setNameLabelText(placesModel->text(index)); m_preview->setPixmap(placesModel->icon(index).pixmap(128, 128)); return true; } } return false; } void InformationPanelContent::setNameLabelText(const QString& text) { QTextOption textOption; textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); const QString processedText = Qt::mightBeRichText(text) ? text : KStringHandler::preProcessWrap(text); QTextLayout textLayout(processedText); textLayout.setFont(m_nameLabel->font()); textLayout.setTextOption(textOption); QString wrappedText; wrappedText.reserve(processedText.length()); // wrap the text to fit into the width of m_nameLabel textLayout.beginLayout(); QTextLine line = textLayout.createLine(); while (line.isValid()) { line.setLineWidth(m_nameLabel->width()); wrappedText += processedText.mid(line.textStart(), line.textLength()); line = textLayout.createLine(); if (line.isValid()) { wrappedText += QChar::LineSeparator; } } textLayout.endLayout(); m_nameLabel->setText(wrappedText); } void InformationPanelContent::adjustWidgetSizes(int width) { // If the text inside the name label or the info label cannot // get wrapped, then the maximum width of the label is increased // so that the width of the information panel gets increased. // To prevent this, the maximum width is adjusted to // the current width of the panel. const int maxWidth = width - KDialog::spacingHint() * 4; m_nameLabel->setMaximumWidth(maxWidth); // The metadata widget also contains a text widget which may return // a large preferred width. if (m_metaDataWidget) { m_metaDataWidget->setMaximumWidth(maxWidth); } // try to increase the preview as large as possible m_preview->setSizeHint(QSize(maxWidth, maxWidth)); if (m_phononWidget->isVisible() && (m_phononWidget->mode() == PhononWidget::Video)) { // assure that the size of the video player is the same as the preview size m_phononWidget->setVideoSize(QSize(maxWidth, maxWidth)); } } #include "informationpanelcontent.moc" 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 @@ -1,58 +1,60 @@ /*************************************************************************** * Copyright (C) 2008 by Peter Penz * * Copyright (C) 2010 by Christian Muehlhaeuser * * * * 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 "placespanel.h" #include #include -#include "views/draganddrophelper.h" PlacesPanel::PlacesPanel(QWidget* parent) : KFilePlacesView(parent), m_mouseButtons(Qt::NoButton) { setDropOnPlaceEnabled(true); connect(this, SIGNAL(urlsDropped(const KUrl&, QDropEvent*, QWidget*)), this, SLOT(slotUrlsDropped(const KUrl&, QDropEvent*, QWidget*))); connect(this, SIGNAL(urlChanged(const KUrl&)), this, SLOT(emitExtendedUrlChangedSignal(const KUrl&))); } PlacesPanel::~PlacesPanel() { } void PlacesPanel::mousePressEvent(QMouseEvent* event) { m_mouseButtons = event->buttons(); KFilePlacesView::mousePressEvent(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) { emit urlChanged(url, m_mouseButtons); } #include "placespanel.moc" 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 @@ -1,97 +1,97 @@ /*************************************************************************** * Copyright (C) 2007 by Peter Penz (peter.penz@gmx.at) * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "additionalinfodialog.h" #include #include #include #include #include "views/additionalinfoaccessor.h" AdditionalInfoDialog::AdditionalInfoDialog(QWidget* parent, - KFileItemDelegate::InformationList infoList) : + const QList& infoList) : KDialog(parent), m_infoList(infoList), m_checkBoxes() { setCaption(i18nc("@title:window", "Additional Information")); setButtons(Ok | Cancel); setDefaultButton(Ok); QWidget* mainWidget = new QWidget(this); mainWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); QVBoxLayout* layout = new QVBoxLayout(mainWidget); // Add header QLabel* header = new QLabel(mainWidget); header->setText(i18nc("@label", "Select which additional information should be shown:")); header->setWordWrap(true); layout->addWidget(header); // Add checkboxes const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); - const KFileItemDelegate::InformationList keys = infoAccessor.keys(); - foreach (const KFileItemDelegate::Information info, keys) { + const QList keys = infoAccessor.keys(); + foreach (DolphinView::AdditionalInfo info, keys) { QCheckBox* checkBox = new QCheckBox(infoAccessor.translation(info), mainWidget); checkBox->setChecked(infoList.contains(info)); layout->addWidget(checkBox); m_checkBoxes.append(checkBox); } layout->addStretch(1); setMainWidget(mainWidget); const KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), "AdditionalInfoDialog"); restoreDialogSize(dialogConfig); connect(this, SIGNAL(okClicked()), this, SLOT(slotOk())); } AdditionalInfoDialog::~AdditionalInfoDialog() { KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), "AdditionalInfoDialog"); saveDialogSize(dialogConfig, KConfigBase::Persistent); } -KFileItemDelegate::InformationList AdditionalInfoDialog::informationList() const +QList AdditionalInfoDialog::informationList() const { return m_infoList; } void AdditionalInfoDialog::slotOk() { m_infoList.clear(); - const KFileItemDelegate::InformationList keys = AdditionalInfoAccessor::instance().keys(); + const QList 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); } ++index; } } #include "additionalinfodialog.moc" 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 @@ -1,49 +1,49 @@ /*************************************************************************** * Copyright (C) 2007 by Peter Penz (peter.penz@gmx.at) * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #ifndef ADDITIONALINFODIALOG_H #define ADDITIONALINFODIALOG_H +#include #include -#include #include class QCheckBox; /** * @brief Dialog for changing the additional information properties of a directory. */ class AdditionalInfoDialog : public KDialog { Q_OBJECT public: - AdditionalInfoDialog(QWidget* parent, KFileItemDelegate::InformationList infoList); + AdditionalInfoDialog(QWidget* parent, const QList& infoList); virtual ~AdditionalInfoDialog(); - KFileItemDelegate::InformationList informationList() const; + QList informationList() const; private slots: void slotOk(); private: - KFileItemDelegate::InformationList m_infoList; + QList m_infoList; QList m_checkBoxes; }; #endif 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 @@ -1,81 +1,81 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * * * The code is based on kdelibs/kio/directorysizejob.* * * (C) 2006 by David Faure * * * * 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 "applyviewpropsjob.h" #include ApplyViewPropsJob::ApplyViewPropsJob(const KUrl& dir, const ViewProperties& viewProps) : KIO::Job(), m_viewProps(0), m_progress(0), m_dir(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()); KIO::ListJob* listJob = KIO::listRecursive(dir, KIO::HideProgressInfo); connect(listJob, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList&)), SLOT(slotEntries(KIO::Job*, const KIO::UDSEntryList&))); addSubjob(listJob); } ApplyViewPropsJob::~ApplyViewPropsJob() { delete m_viewProps; // the properties are written by the destructor m_viewProps = 0; } void ApplyViewPropsJob::slotEntries(KIO::Job*, const KIO::UDSEntryList& list) { KIO::UDSEntryList::ConstIterator it = list.begin(); const KIO::UDSEntryList::ConstIterator end = list.end(); foreach(const KIO::UDSEntry& entry, list) { const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME); if ((name != ".") && (name != "..") && entry.isDir()) { ++m_progress; KUrl url(m_dir); url.addPath(name); Q_ASSERT(m_viewProps); ViewProperties props(url); props.setDirProperties(*m_viewProps); } } } void ApplyViewPropsJob::slotResult(KJob* job) { if (job->error()) { setError(job->error()); setErrorText(job->errorText()); } emitResult(); } #include "applyviewpropsjob.moc" 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 similarity index 79% rename from src/settings/dolphin_columnmodesettings.kcfg rename to src/settings/dolphin_compactmodesettings.kcfg index 963d326cc..bb050d480 100644 --- a/src/settings/dolphin_columnmodesettings.kcfg +++ b/src/settings/dolphin_compactmodesettings.kcfg @@ -1,44 +1,40 @@ - kiconloader.h + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd"> kglobalsettings.h + kiconloader.h - - - - KGlobalSettings::generalFont().family() - + true + + + KGlobalSettings::generalFont().family() + KGlobalSettings::generalFont().pointSizeF() false 0 - KIconLoader::SizeSmall + KIconLoader::SizeMedium - KIconLoader::SizeLarge - - - - 250 + KIconLoader::SizeHuge 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 @@ -1,48 +1,44 @@ kiconloader.h kglobalsettings.h KGlobalSettings::generalFont().family() true KGlobalSettings::generalFont().pointSizeF() false 0 KIconLoader::SizeSmall KIconLoader::SizeLarge - - - false - 0,1,2,3,4,5,6,7,8 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 @@ -1,82 +1,73 @@ kfileitemdelegate.h - - + + When this option is enabled hidden files, such as those starting with a '.', will be shown in the file view. false This option defines the used version of the view properties. - 1 + 2 1 This option controls the style of the view. Currently supported values include icons (0), details (1) and column (2) views. DolphinView::IconsView - 0 - DolphinView::MaxModeEnum - - + + When this option is enabled, a preview of the file content is shown as an icon. false When this option is enabled, the sorted items are summarized by their category. false This option defines which attribute (name, size, date, etc.) sorting is performed on. DolphinView::SortByName - 0 - DolphinView::MaxSortingEnum Qt::AscendingOrder Qt::AscendingOrder Qt::DescendingOrder true - - - 0 - - - + The last time these properties were changed by the user. 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 @@ -1,70 +1,44 @@ kglobalsettings.h kiconloader.h - QListView - - - QListView::TopToBottom - true KGlobalSettings::generalFont().family() KGlobalSettings::generalFont().pointSizeF() false 0 - - - - KIconLoader::SizeMedium + QFontMetrics(KGlobalSettings::generalFont()).height() * 2 + 10 - - - - - 96 - - - - 8 - KIconLoader::SizeMedium - - - - 2 - KIconLoader::SizeHuge + + + 1 + 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 @@ -1,78 +1,62 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz (peter.penz@gmx.at), * * Cvetoslav Ludmiloff and Patrice Tremblay * * * * 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 "dolphinsettings.h" #include #include #include #include -#include "dolphin_columnmodesettings.h" #include "dolphin_detailsmodesettings.h" #include "dolphin_generalsettings.h" #include "dolphin_iconsmodesettings.h" class DolphinSettingsSingleton { public: DolphinSettings instance; }; K_GLOBAL_STATIC(DolphinSettingsSingleton, s_settings) DolphinSettings& DolphinSettings::instance() { return s_settings->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(); } 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 @@ -1,93 +1,71 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * peter.penz@gmx.at * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #ifndef DOLPHINSETTINGS_H #define DOLPHINSETTINGS_H #include -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. * * The following properties are stored: * - home URL * - default view mode * - URL navigator state (editable or not) * - Text completion state * - split view * - properties for views */ class LIBDOLPHINPRIVATE_EXPORT DolphinSettings { public: static DolphinSettings& instance(); GeneralSettings* generalSettings() const; - IconsModeSettings* iconsModeSettings() const; - DetailsModeSettings* detailsModeSettings() const; - ColumnModeSettings* columnModeSettings() const; KFilePlacesModel* placesModel() const; virtual void save(); protected: DolphinSettings(); virtual ~DolphinSettings(); friend class DolphinSettingsSingleton; private: GeneralSettings* m_generalSettings; - IconsModeSettings* m_iconsModeSettings; - DetailsModeSettings* m_detailsModeSettings; - ColumnModeSettings* m_columnModeSettings; KFilePlacesModel* m_placesModel; }; 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; } #endif 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 @@ -1,105 +1,100 @@ /*************************************************************************** * Copyright (C) 2008 by Peter Penz * * * * 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 "kcmdolphinviewmodes.h" #include #include #include #include #include -#include #include #include #include #include #include #include #include K_PLUGIN_FACTORY(KCMDolphinViewModesConfigFactory, registerPlugin("dolphinviewmodes");) K_EXPORT_PLUGIN(KCMDolphinViewModesConfigFactory("kcmdolphinviewmodes")) DolphinViewModesConfigModule::DolphinViewModesConfigModule(QWidget* parent, const QVariantList& args) : KCModule(KCMDolphinViewModesConfigFactory::componentData(), parent), m_pages() { Q_UNUSED(args); KGlobal::locale()->insertCatalog("dolphin"); setButtons(KCModule::Default | KCModule::Help); QVBoxLayout* topLayout = new QVBoxLayout(this); topLayout->setMargin(0); topLayout->setSpacing(KDialog::spacingHint()); KTabWidget* tabWidget = new KTabWidget(this); // initialize 'Icons' tab IconsViewSettingsPage* iconsPage = new IconsViewSettingsPage(tabWidget); 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); } DolphinViewModesConfigModule::~DolphinViewModesConfigModule() { } void DolphinViewModesConfigModule::save() { foreach (ViewSettingsPageBase* page, m_pages) { page->applySettings(); } reparseConfiguration(); } void DolphinViewModesConfigModule::defaults() { foreach (ViewSettingsPageBase* page, m_pages) { page->restoreDefaults(); } reparseConfiguration(); } void DolphinViewModesConfigModule::reparseConfiguration() { QDBusMessage message = QDBusMessage::createSignal("/KonqMain", "org.kde.Konqueror.Main", "reparseConfiguration"); QDBusConnection::sessionBus().send(message); } #include "kcmdolphinviewmodes.moc" 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 * - * * - * 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 -#include "iconsizegroupbox.h" - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -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 @@ -1,151 +1,135 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * * * 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 "detailsviewsettingspage.h" #include "iconsizegroupbox.h" #include "dolphinfontrequester.h" #include "dolphin_detailsmodesettings.h" #include #include -#include - #include #include #include #include #include #include #include #include #include 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(); 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 QWidget* 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); QHBoxLayout* textLayout = new QHBoxLayout(textGroup); 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. 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_expandableFolders, SIGNAL(toggled(bool)), this, SIGNAL(changed())); } 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 @@ -1,60 +1,58 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * * * 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 DETAILSVIEWSETTINGSPAGE_H #define DETAILSVIEWSETTINGSPAGE_H #include "viewsettingspagebase.h" class DolphinFontRequester; class IconSizeGroupBox; -class QCheckBox; /** * @brief Represents the page from the Dolphin Settings which allows * to modify the settings for the details view. */ class DetailsViewSettingsPage : public ViewSettingsPageBase { Q_OBJECT public: DetailsViewSettingsPage(QWidget* parent); virtual ~DetailsViewSettingsPage(); /** * 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(); 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 @@ -1,246 +1,153 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * * * 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 "iconsviewsettingspage.h" #include "dolphinfontrequester.h" -#include "settings/dolphinsettings.h" #include "iconsizegroupbox.h" #include "dolphin_iconsmodesettings.h" #include #include #include #include #include #include #include #include #include -#include #include #include -#include #include 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(); 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' 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); 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")); 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(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. 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_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() { } 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; - } - - settings->setItemWidth(itemWidth); - settings->setItemHeight(itemHeight); - - settings->setUseSystemFont(m_fontRequester->mode() == DolphinFontRequester::SystemFont); - settings->setFontFamily(font.family()); - settings->setFontSize(font.pointSizeF()); - settings->setItalicFont(font.italic()); - settings->setFontWeight(font.weight()); + IconsModeSettings::setUseSystemFont(m_fontRequester->mode() == DolphinFontRequester::SystemFont); + IconsModeSettings::setFontFamily(font.family()); + IconsModeSettings::setFontSize(font.pointSizeF()); + IconsModeSettings::setItalicFont(font.italic()); + IconsModeSettings::setFontWeight(font.weight()); - settings->setNumberOfTextlines(numberOfTextlines); + IconsModeSettings::setTextWidthIndex(m_textWidthBox->currentIndex()); - 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 @@ -1,87 +1,69 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * * * 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 ICONSVIEWSETTINGSPAGE_H #define ICONSVIEWSETTINGSPAGE_H -#include #include "viewsettingspagebase.h" class DolphinFontRequester; class IconSizeGroupBox; class KComboBox; class KIntSpinBox; /** * @brief Tab page for the 'Icons Mode' settings * of the Dolphin settings dialog. * * Allows to set: * - icon size * - preview size * - text width - * - grid spacing * - font - * - number of text lines - * - arrangement * * @see DolphinIconsViewSettings */ class IconsViewSettingsPage : public ViewSettingsPageBase { Q_OBJECT public: IconsViewSettingsPage(QWidget* parent); virtual ~IconsViewSettingsPage(); /** * Applies the settings for the icons view. * The settings are persisted automatically when * closing Dolphin. */ virtual void applySettings(); /** Restores the settings to default values. */ virtual void restoreDefaults(); 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 @@ -1,84 +1,79 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * peter.penz@gmx.at * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "viewsettingspage.h" -#include "columnviewsettingspage.h" #include "iconsviewsettingspage.h" #include "detailsviewsettingspage.h" #include #include #include #include #include ViewSettingsPage::ViewSettingsPage(QWidget* parent) : SettingsPageBase(parent), m_pages() { QVBoxLayout* topLayout = new QVBoxLayout(this); topLayout->setMargin(0); topLayout->setSpacing(KDialog::spacingHint()); KTabWidget* tabWidget = new KTabWidget(this); // initialize 'Icons' tab IconsViewSettingsPage* iconsPage = new IconsViewSettingsPage(tabWidget); 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); } ViewSettingsPage::~ViewSettingsPage() { } void ViewSettingsPage::applySettings() { foreach (ViewSettingsPageBase* page, m_pages) { page->applySettings(); } } void ViewSettingsPage::restoreDefaults() { foreach (ViewSettingsPageBase* page, m_pages) { page->restoreDefaults(); } } #include "viewsettingspage.moc" 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 @@ -1,421 +1,418 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * peter.penz@gmx.at * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "viewpropertiesdialog.h" #include "additionalinfodialog.h" #include "views/dolphinview.h" #include "settings/dolphinsettings.h" #include "dolphin_generalsettings.h" #include "dolphin_iconsmodesettings.h" #include "viewpropsprogressinfo.h" #include #ifdef HAVE_NEPOMUK #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include ViewPropertiesDialog::ViewPropertiesDialog(DolphinView* dolphinView) : KDialog(dolphinView), m_isDirty(false), m_dolphinView(dolphinView), m_viewProps(0), m_viewMode(0), m_sortOrder(0), m_sorting(0), m_sortFoldersFirst(0), - m_showPreview(0), + m_previewsShown(0), m_showInGroups(0), m_showHiddenFiles(0), m_additionalInfo(0), m_applyToCurrentFolder(0), m_applyToSubFolders(0), m_applyToAllFolders(0), m_useAsDefault(0) { Q_ASSERT(dolphinView); const bool useGlobalViewProps = DolphinSettings::instance().generalSettings()->globalViewProps(); setCaption(i18nc("@title:window", "View Properties")); setButtons(KDialog::Ok | KDialog::Cancel | KDialog::Apply); const KUrl& url = dolphinView->url(); m_viewProps = new ViewProperties(url); m_viewProps->setAutoSaveEnabled(false); QWidget* main = new QWidget(); QVBoxLayout* topLayout = new QVBoxLayout(); // create 'Properties' group containing view mode, sorting, sort order and show hidden files QWidget* propsBox = main; if (!useGlobalViewProps) { propsBox = new QGroupBox(i18nc("@title:group", "Properties"), main); } QWidget* propsGrid = new QWidget(); 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); m_sortOrder = new KComboBox(sortingBox); m_sortOrder->addItem(i18nc("@item:inlistbox Sort", "Ascending")); m_sortOrder->addItem(i18nc("@item:inlistbox Sort", "Descending")); m_sorting = new KComboBox(sortingBox); m_sorting->addItem(i18nc("@item:inlistbox Sort", "By Name")); m_sorting->addItem(i18nc("@item:inlistbox Sort", "By Size")); m_sorting->addItem(i18nc("@item:inlistbox Sort", "By Date")); m_sorting->addItem(i18nc("@item:inlistbox Sort", "By Permissions")); m_sorting->addItem(i18nc("@item:inlistbox Sort", "By Owner")); m_sorting->addItem(i18nc("@item:inlistbox Sort", "By Group")); m_sorting->addItem(i18nc("@item:inlistbox Sort", "By Type")); #ifdef HAVE_NEPOMUK // TODO: Hided "sort by rating" and "sort by tags" as without caching the performance // is too slow currently (Nepomuk will support caching in future releases). // // if (!Nepomuk::ResourceManager::instance()->init()) { // m_sorting->addItem(i18nc("@item:inlistbox Sort", "By Rating")); // m_sorting->addItem(i18nc("@item:inlistbox Sort", "By Tags")); // } #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")); m_additionalInfo = new QPushButton(i18nc("@action:button", "Additional Information")); QHBoxLayout* sortingLayout = new QHBoxLayout(); sortingLayout->setMargin(0); sortingLayout->addWidget(m_sortOrder); sortingLayout->addWidget(m_sorting); sortingBox->setLayout(sortingLayout); QGridLayout* propsGridLayout = new QGridLayout(propsGrid); propsGridLayout->addWidget(viewModeLabel, 0, 0, Qt::AlignRight); propsGridLayout->addWidget(m_viewMode, 0, 1); propsGridLayout->addWidget(sortingLabel, 1, 0, Qt::AlignRight); propsGridLayout->addWidget(sortingBox, 1, 1); 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); topLayout->addWidget(propsBox); connect(m_viewMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotViewModeChanged(int))); connect(m_sorting, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSortingChanged(int))); connect(m_sortOrder, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSortOrderChanged(int))); connect(m_additionalInfo, SIGNAL(clicked()), 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())); connect(m_showHiddenFiles, SIGNAL(clicked()), this, SLOT(slotShowHiddenFilesChanged())); connect(this, SIGNAL(okClicked()), this, SLOT(slotOk())); connect(this, SIGNAL(applyClicked()), this, SLOT(slotApply())); // Only show the following settings if the view properties are remembered // for each directory: if (!useGlobalViewProps) { // create 'Apply View Properties To' group QGroupBox* applyBox = new QGroupBox(i18nc("@title:group", "Apply View Properties To"), main); m_applyToCurrentFolder = new QRadioButton(i18nc("@option:radio Apply View Properties To", "Current folder"), applyBox); m_applyToCurrentFolder->setChecked(true); m_applyToSubFolders = new QRadioButton(i18nc("@option:radio Apply View Properties To", "Current folder including all sub-folders"), applyBox); m_applyToAllFolders = new QRadioButton(i18nc("@option:radio Apply View Properties To", "All folders"), applyBox); QButtonGroup* applyGroup = new QButtonGroup(this); applyGroup->addButton(m_applyToCurrentFolder); applyGroup->addButton(m_applyToSubFolders); applyGroup->addButton(m_applyToAllFolders); QVBoxLayout* applyBoxLayout = new QVBoxLayout(applyBox); applyBoxLayout->addWidget(m_applyToCurrentFolder); applyBoxLayout->addWidget(m_applyToSubFolders); applyBoxLayout->addWidget(m_applyToAllFolders); m_useAsDefault = new QCheckBox(i18nc("@option:check", "Use these view properties as default"), main); topLayout->addWidget(applyBox); topLayout->addWidget(m_useAsDefault); connect(m_applyToCurrentFolder, SIGNAL(clicked(bool)), this, SLOT(markAsDirty(bool))); connect(m_applyToSubFolders, SIGNAL(clicked(bool)), this, SLOT(markAsDirty(bool))); connect(m_applyToAllFolders, SIGNAL(clicked(bool)), this, SLOT(markAsDirty(bool))); connect(m_useAsDefault, SIGNAL(clicked(bool)), this, SLOT(markAsDirty(bool))); } main->setLayout(topLayout); setMainWidget(main); const KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), "ViewPropertiesDialog"); restoreDialogSize(dialogConfig); loadSettings(); } ViewPropertiesDialog::~ViewPropertiesDialog() { m_isDirty = false; delete m_viewProps; m_viewProps = 0; KConfigGroup dialogConfig(KSharedConfig::openConfig("dolphinrc"), "ViewPropertiesDialog"); saveDialogSize(dialogConfig, KConfigBase::Persistent); } void ViewPropertiesDialog::slotOk() { applyViewProperties(); accept(); } void ViewPropertiesDialog::slotApply() { applyViewProperties(); markAsDirty(false); } void ViewPropertiesDialog::slotViewModeChanged(int index) { m_viewProps->setViewMode(static_cast(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) { const Qt::SortOrder sortOrder = (index == 0) ? Qt::AscendingOrder : Qt::DescendingOrder; m_viewProps->setSortOrder(sortOrder); markAsDirty(true); } void ViewPropertiesDialog::slotCategorizedSortingChanged() { m_viewProps->setCategorizedSorting(m_showInGroups->isChecked()); markAsDirty(true); } void ViewPropertiesDialog::slotSortFoldersFirstChanged() { const bool foldersFirst = m_sortFoldersFirst->isChecked(); m_viewProps->setSortFoldersFirst(foldersFirst); markAsDirty(true); } 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); } void ViewPropertiesDialog::markAsDirty(bool isDirty) { m_isDirty = isDirty; enableButtonApply(isDirty); } void ViewPropertiesDialog::configureAdditionalInfo() { - KFileItemDelegate::InformationList info = m_viewProps->additionalInfo(); + QList 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 dialog = new AdditionalInfoDialog(this, info); + QPointer dialog = new AdditionalInfoDialog(this, infoList); if (dialog->exec() == QDialog::Accepted) { - m_viewProps->setAdditionalInfo(dialog->informationList()); + m_viewProps->setAdditionalInfoList(dialog->informationList()); markAsDirty(true); } delete dialog; } void ViewPropertiesDialog::applyViewProperties() { // if nothing changed in the dialog, we have nothing to apply if (!m_isDirty) { return; } const bool applyToSubFolders = m_applyToSubFolders && m_applyToSubFolders->isChecked(); if (applyToSubFolders) { const QString text(i18nc("@info", "The view properties of all sub-folders will be changed. Do you want to continue?")); if (KMessageBox::questionYesNo(this, text) == KMessageBox::No) { return; } ViewPropsProgressInfo* info = new ViewPropsProgressInfo(m_dolphinView, m_dolphinView->url(), *m_viewProps); info->setAttribute(Qt::WA_DeleteOnClose); info->setWindowModality(Qt::NonModal); info->show(); } const bool applyToAllFolders = m_applyToAllFolders && m_applyToAllFolders->isChecked(); // If the user selected 'Apply To All Folders' the view properties implicitely // are also used as default for new folders. const bool useAsDefault = applyToAllFolders || (m_useAsDefault && m_useAsDefault->isChecked()); if (useAsDefault) { // For directories where no .directory file is available, the .directory // file stored for the global view properties is used as fallback. To update // this file we temporary turn on the global view properties mode. GeneralSettings* settings = DolphinSettings::instance().generalSettings(); Q_ASSERT(!settings->globalViewProps()); settings->setGlobalViewProps(true); ViewProperties defaultProps(m_dolphinView->url()); defaultProps.setDirProperties(*m_viewProps); defaultProps.save(); settings->setGlobalViewProps(false); } if (applyToAllFolders) { const QString text(i18nc("@info", "The view properties of all folders will be changed. Do you want to continue?")); if (KMessageBox::questionYesNo(this, text) == KMessageBox::No) { return; } // Updating the global view properties time stamp in the general settings makes // all existing viewproperties invalid, as they have a smaller time stamp. GeneralSettings* settings = DolphinSettings::instance().generalSettings(); settings->setViewPropsTimestamp(QDateTime::currentDateTime()); } m_dolphinView->setMode(m_viewProps->viewMode()); m_dolphinView->setSorting(m_viewProps->sorting()); 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(); markAsDirty(false); } void ViewPropertiesDialog::loadSettings() { // load view mode const int index = static_cast(m_viewProps->viewMode()); m_viewMode->setCurrentIndex(index); // load sort order and sorting const int sortOrderIndex = (m_viewProps->sortOrder() == Qt::AscendingOrder) ? 0 : 1; m_sortOrder->setCurrentIndex(sortOrderIndex); m_sorting->setCurrentIndex(m_viewProps->sorting()); const bool enabled = (index == DolphinView::DetailsView) || (index == DolphinView::IconsView); m_additionalInfo->setEnabled(enabled); 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); } #include "viewpropertiesdialog.moc" 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 @@ -1,86 +1,86 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * peter.penz@gmx.at * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #ifndef VIEWPROPERTIESDIALOG_H #define VIEWPROPERTIESDIALOG_H #include "libdolphin_export.h" #include class QCheckBox; class KComboBox; class QPushButton; class QRadioButton; class ViewProperties; class DolphinView; /** * @brief Dialog for changing the current view properties of a directory. * * It is possible to specify the view mode, the sorting order, whether hidden files * and previews should be shown. The properties can be assigned to the current folder, * or recursively to all sub folders. */ class LIBDOLPHINPRIVATE_EXPORT ViewPropertiesDialog : public KDialog { Q_OBJECT public: explicit ViewPropertiesDialog(DolphinView* dolphinView); virtual ~ViewPropertiesDialog(); private slots: void slotOk(); void slotApply(); void slotViewModeChanged(int index); void slotSortingChanged(int index); void slotSortOrderChanged(int index); void slotCategorizedSortingChanged(); void slotSortFoldersFirstChanged(); void slotShowPreviewChanged(); void slotShowHiddenFilesChanged(); void markAsDirty(bool isDirty); void configureAdditionalInfo(); private: void applyViewProperties(); void loadSettings(); private: bool m_isDirty; DolphinView* m_dolphinView; ViewProperties* m_viewProps; KComboBox* m_viewMode; KComboBox* m_sortOrder; KComboBox* m_sorting; QCheckBox* m_sortFoldersFirst; - QCheckBox* m_showPreview; + QCheckBox* m_previewsShown; QCheckBox* m_showInGroups; QCheckBox* m_showHiddenFiles; QPushButton* m_additionalInfo; QRadioButton* m_applyToCurrentFolder; QRadioButton* m_applyToSubFolders; QRadioButton* m_applyToAllFolders; QCheckBox* m_useAsDefault; }; #endif 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 @@ -1,402 +1,371 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * peter.penz@gmx.at * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "dolphinstatusbar.h" #include "dolphin_generalsettings.h" #include #include #include #include #include #include "settings/dolphinsettings.h" #include "statusbarspaceinfo.h" #include #include #include #include #include #include #include #include #include #include DolphinStatusBar::DolphinStatusBar(QWidget* parent, DolphinView* view) : QWidget(parent), 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), m_showProgressBarTimer(0), m_messageTimeStamp() { connect(m_view, SIGNAL(urlChanged(const KUrl&)), this, SLOT(updateSpaceInfoContent(const KUrl&))); // 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(); const int max = ZoomLevelInfo::maximumLevel(); m_zoomSlider->setRange(min, max); 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); m_spaceInfo->setUrl(m_view->url()); // Initialize progress information m_stopButton = new QToolButton(this); m_stopButton->setIcon(KIcon("process-stop")); // TODO: Add tooltip for KDE SC 4.7.0, if new strings are allowed again m_stopButton->setAutoRaise(true); m_stopButton->hide(); connect(m_stopButton, SIGNAL(clicked()), this, SIGNAL(stopPressed())); m_progressText = new QLabel(this); m_progressText->hide(); m_progressBar = new QProgressBar(this); m_progressBar->hide(); m_showProgressBarTimer = new QTimer(this); m_showProgressBarTimer->setInterval(1000); m_showProgressBarTimer->setSingleShot(true); connect(m_showProgressBarTimer, SIGNAL(timeout()), this, SLOT(updateProgressInfo())); // 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); m_spaceInfo->setMaximumSize(200, contentHeight - 5); 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); topLayout->addWidget(m_progressBar); setExtensionsVisible(true); } DolphinStatusBar::~DolphinStatusBar() { } void DolphinStatusBar::setMessage(const QString& msg, Type type) { int timeout = 1000; // Timeout in milliseconds until default // messages may overwrite other messages. QString message = msg; if (message.isEmpty()) { // Show the default text as fallback. An empty text indicates // a clearing of the information message. if (m_messageLabel->defaultText().isEmpty()) { return; } message = m_messageLabel->defaultText(); type = Default; timeout = 0; } KonqStatusBarMessageLabel::Type konqType = static_cast(type); if ((message == m_messageLabel->text()) && (konqType == m_messageLabel->type())) { // the message is already shown return; } const QTime currentTime = QTime::currentTime(); const bool skipMessage = (type == Default) && m_messageTimeStamp.isValid() && (m_messageTimeStamp.msecsTo(currentTime) < timeout); if (skipMessage) { // A non-default message is shown just for a very short time. Don't hide // the message by a default message, so that the user gets the chance to // read the information. return; } m_messageLabel->setMessage(message, konqType); if (type != Default) { m_messageTimeStamp = currentTime; } } DolphinStatusBar::Type DolphinStatusBar::type() const { return static_cast(m_messageLabel->type()); } QString DolphinStatusBar::message() const { return m_messageLabel->text(); } void DolphinStatusBar::setProgressText(const QString& text) { m_progressText->setText(text); } int DolphinStatusBar::progress() const { return m_progress; } QString DolphinStatusBar::progressText() const { return m_progressText->text(); } void DolphinStatusBar::setProgress(int percent) { // Show a busy indicator if a value < 0 is provided: m_progressBar->setMaximum((percent < 0) ? 0 : 100); if (percent < 0) { percent = 0; } else if (percent > 100) { percent = 100; } const bool progressRestarted = (percent < 100) && (percent < m_progress); m_progress = percent; if (m_messageLabel->type() == KonqStatusBarMessageLabel::Error) { // Don't update any widget or status bar text if an // error message is shown return; } if (progressRestarted && !m_progressBar->isVisible()) { // Show the progress bar delayed: In the case if 100 % are reached within // a short time, no progress bar will be shown at all. m_showProgressBarTimer->start(); } m_progressBar->setValue(m_progress); if (percent == 100) { // The end of the progress has been reached. Assure that the progress bar // gets hidden and the extensions widgets get visible again. m_showProgressBarTimer->stop(); updateProgressInfo(); } const QString defaultText = m_messageLabel->defaultText(); const QString msg(m_messageLabel->text()); if ((percent == 0) && !msg.isEmpty()) { setMessage(QString(), Default); } else if ((percent == 100) && (msg != defaultText)) { setMessage(defaultText, Default); } } void DolphinStatusBar::clear() { setMessage(m_messageLabel->defaultText(), Default); } void DolphinStatusBar::setDefaultText(const QString& text) { m_messageLabel->setDefaultText(text); } QString DolphinStatusBar::defaultText() const { return m_messageLabel->defaultText(); } void DolphinStatusBar::refresh() { setExtensionsVisible(true); } void DolphinStatusBar::contextMenuEvent(QContextMenuEvent* event) { Q_UNUSED(event); KMenu menu(this); QAction* copyAction = 0; switch (type()) { case Default: case OperationCompleted: case Information: copyAction = menu.addAction(i18nc("@action:inmenu", "Copy Information Message")); break; case Error: copyAction = menu.addAction(i18nc("@action:inmenu", "Copy Error Message")); break; default: break; } GeneralSettings* settings = DolphinSettings::instance().generalSettings(); QAction* showZoomSliderAction = menu.addAction(i18nc("@action:inmenu", "Show Zoom Slider")); showZoomSliderAction->setCheckable(true); showZoomSliderAction->setChecked(settings->showZoomSlider()); QAction* showSpaceInfoAction = menu.addAction(i18nc("@action:inmenu", "Show Space Information")); showSpaceInfoAction->setCheckable(true); showSpaceInfoAction->setChecked(settings->showSpaceInfo()); const QAction* action = menu.exec(QCursor::pos()); if (action == copyAction) { QMimeData* mimeData = new QMimeData(); mimeData->setText(message()); QApplication::clipboard()->setMimeData(mimeData); } 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); m_spaceInfo->setVisible(visible); } } void DolphinStatusBar::updateSpaceInfoContent(const KUrl& url) { m_spaceInfo->setUrl(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); QPoint global = m_zoomSlider->rect().topLeft(); global.ry() += m_zoomSlider->height() / 2; QHelpEvent toolTipEvent(QEvent::ToolTip, QPoint(0, 0), m_zoomSlider->mapToGlobal(global)); 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); if (m_progress < 100) { // Show the progress information and hide the extensions setExtensionsVisible(false); if (!isErrorShown) { m_stopButton->show(); m_progressText->show(); m_progressBar->show(); } } else { // Hide the progress information and show the extensions m_stopButton->hide(); m_progressText->hide(); m_progressBar->hide(); setExtensionsVisible(true); } } 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) { const int size = ZoomLevelInfo::iconSizeForZoomLevel(zoomLevel); m_zoomSlider->setToolTip(i18ncp("@info:tooltip", "Size: 1 pixel", "Size: %1 pixels", size)); } #include "dolphinstatusbar.moc" 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 @@ -1,183 +1,179 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * peter.penz@gmx.at * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #ifndef DOLPHINSTATUSBAR_H #define DOLPHINSTATUSBAR_H #include "konq_statusbarmessagelabel.h" #include #include class DolphinView; class KUrl; class StatusBarSpaceInfo; class QLabel; class QProgressBar; class QToolButton; class QSlider; class QTimer; /** * @brief Represents the statusbar of a Dolphin view. * * The statusbar allows to show messages and progress * information. */ class DolphinStatusBar : public QWidget { Q_OBJECT public: /** * Describes the type of the message text. Dependent * from the type a corresponding icon and color is * used for the message text. */ enum Type { Default = KonqStatusBarMessageLabel::Default, OperationCompleted = KonqStatusBarMessageLabel::OperationCompleted, Information = KonqStatusBarMessageLabel::Information, Error = KonqStatusBarMessageLabel::Error }; DolphinStatusBar(QWidget* parent, DolphinView* view); virtual ~DolphinStatusBar(); /** * Sets the message text to \a msg. Dependant * from the given type \a type an icon is shown and * the color of the text is adjusted. The height of * the statusbar is automatically adjusted in a way, * that the full text fits into the available width. * * If a progress is ongoing and a message * with the type Type::Error is set, the progress * is cleared automatically. */ void setMessage(const QString& msg, Type type); QString message() const; Type type() const; /** * Sets the text for the progress information. * DolphinStatusBar::setProgress() should be invoked * afterwards each time the progress changes. */ void setProgressText(const QString& text); QString progressText() const; /** * Sets the progress in percent (0 - 100). The * progress is shown delayed by 1 second: * If the progress does reach 100 % within 1 second, * the progress is not shown at all. */ void setProgress(int percent); int progress() const; /** * Clears the message text of the status bar by replacing * the message with the default text, which can be set * by DolphinStatusBar::setDefaultText(). The progress * information is not cleared. */ void clear(); /** * Sets the default text, which is shown if the status bar * is cleared by DolphinStatusBar::clear(). */ void setDefaultText(const QString& text); QString defaultText() const; /** * Refreshes the status bar to get synchronized with the (updated) Dolphin settings. */ void refresh(); signals: /** * Is emitted if the stop-button has been pressed during showing a progress. */ void stopPressed(); protected: /** @see QWidget::contextMenuEvent() */ virtual void contextMenuEvent(QContextMenuEvent* event); private slots: /** * Is invoked, when the URL of the DolphinView, where the * statusbar belongs too, has been changed. The space information * content is updated. */ void updateSpaceInfoContent(const KUrl& url); /** * Sets the zoom level of the item view to \a zoomLevel. */ void setZoomLevel(int zoomLevel); - void zoomOut(); - void zoomIn(); void showZoomSliderToolTip(int zoomLevel); + void slotZoomLevelChanged(int current, int previous); void updateProgressInfo(); private: /** * Makes the space information widget and zoom slider widget * visible, if \a visible is true and the settings allow to show * the widgets. showUnknownProgressIf \a visible is false, it is assured that both * widgets are hidden. */ void setExtensionsVisible(bool visible); /** * Updates the text of the zoom slider tooltip to show * the currently used size. */ void updateZoomSliderToolTip(int zoomLevel); private: DolphinView* m_view; KonqStatusBarMessageLabel* m_messageLabel; StatusBarSpaceInfo* m_spaceInfo; - QWidget* m_zoomWidget; - QToolButton* m_zoomOut; QSlider* m_zoomSlider; - QToolButton* m_zoomIn; QLabel* m_progressText; QProgressBar* m_progressBar; QToolButton* m_stopButton; int m_progress; QTimer* m_showProgressBarTimer; // Timestamp when the last message has been set that has not the type // 'Default'. The timestamp is used to prevent that default messages // hide more important messages after a very short delay. QTime m_messageTimeStamp; }; #endif 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,37 +1,38 @@ 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) set(dolphinsearchboxtest_SRCS dolphinsearchboxtest.cpp ../search/dolphinsearchbox.cpp ../search/dolphinsearchinformation.cpp ) kde4_add_kcfg_files(dolphinsearchboxtest_SRCS ../search/dolphin_searchsettings.kcfgc ) 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 (frank78ac@googlemail.com) * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ - -#include - -#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 -#include - -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(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 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 (frank78ac@googlemail.com) * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - *****************************************************************************/ - -#include -#include -#include - -#include "views/dolphintreeview.h" - -#include -#include -#include - -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 (frank78ac@googlemail.com) * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - *****************************************************************************/ - -#include - -#include "dolphinviewtest_allviewmodes.h" - -#include - -#include "testdir.h" - -#include "views/dolphinview.h" -#include "views/dolphinmodel.h" -#include "views/dolphindirlister.h" -#include "views/dolphinsortfilterproxymodel.h" -#include "views/zoomlevelinfo.h" - -#include - -#include -#include - -DolphinViewTest_AllViewModes::DolphinViewTest_AllViewModes() { - // Need to register KFileItemList for use with QSignalSpy - qRegisterMetaType("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(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(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 (frank78ac@googlemail.com) * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - *****************************************************************************/ - -#ifndef DOLPHINVIEWTEST_ALLVIEWMODES -#define DOLPHINVIEWTEST_ALLVIEWMODES - -#include "testbase.h" - -#include "views/dolphinview.h" - -/** - * DolphinViewTest_AllViewModes is used as a base class for tests that check the - * basic functionality of DolphinView in all view modes. The derived classes - * have to provide implementations for the virtual methods mode() and verifyCorrectViewMode(), - * see below. - * - * Tests for DolphinView functionality that is specific to a particular view mode or - * to switching between different view modes should not be added here, but to another - * DolphinView unit test. - */ - -class DolphinViewTest_AllViewModes : public TestBase -{ - Q_OBJECT - -public: - - DolphinViewTest_AllViewModes(); - -private slots: - - void testSelection(); - void testViewPropertySettings(); - void testZoomLevel(); - void testSaveAndRestoreState(); - void testKeyboardFocus(); - void testCutCopyPaste(); - -private: - - /** - * Sets the correct view mode, shows the view on the screen, and waits until loading the - * folder in the view is finished. - * - * Many unit tests need access to DolphinVie's internal item view (icons, details, or columns). - * Therefore, a pointer to the item view is returned by initView(DolphinView*). - */ - QAbstractItemView* initView(DolphinView* view) const; - - /** Returns the view mode (Icons, Details, Columns) to be used in the test. */ - virtual DolphinView::Mode mode() const = 0; - - /** Should return true if the view mode is correct. */ - virtual bool verifyCorrectViewMode(const DolphinView* view) const = 0; - - /** - * Waits for the DolphinView's selectionChanged(const KFileItemList&) to be emitted - * and verifies that the number of selected items is as expected. - */ - void verifySelectedItemsCount(DolphinView* view, int itemsCount) const; - -}; - -#endif 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 * + * * + * 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 + +#include +#include "kitemviews/kfileitemlistview.h" +#include "kitemviews/kfileitemmodel.h" +#include "testdir.h" + +#include + +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"); + qRegisterMetaType("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 * + * * + * 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 + +#include +#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"); + qRegisterMetaType("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 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 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("urlA"); + QTest::addColumn("urlB"); + QTest::addColumn("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 (frank78ac@googlemail.com) * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - *****************************************************************************/ - -#include "testbase.h" - -#include - -#include "views/dolphinview.h" -#include "views/dolphinmodel.h" -#include "views/dolphindirlister.h" -#include "views/dolphinsortfilterproxymodel.h" - -#include - -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 (frank78ac@googlemail.com) * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - *****************************************************************************/ - -#ifndef TESTBASE_H -#define TESTBASE_H - -#include - -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 @@ -1,98 +1,110 @@ /***************************************************************************** * Copyright (C) 2010-2011 by Frank Reininghaus (frank78ac@googlemail.com) * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "testdir.h" #include #ifdef Q_OS_UNIX #include #else #include #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 struct utimbuf utbuf; utbuf.actime = mtime.toTime_t(); utbuf.modtime = utbuf.actime; utime(QFile::encodeName(path), &utbuf); #elif defined(Q_OS_WIN) struct _utimbuf utbuf; utbuf.actime = mtime.toTime_t(); utbuf.modtime = utbuf.actime; _wutime(reinterpret_cast(path.utf16()), &utbuf); #endif } void TestDir::createFile(const QString& path, const QByteArray& data, const QDateTime& time) { QString absolutePath = path; makePathAbsoluteAndCreateParents(absolutePath); QFile f(absolutePath); f.open(QIODevice::WriteOnly); f.write(data); f.close(); if (time.isValid()) { setTimeStamp(absolutePath, time); } Q_ASSERT(QFile::exists(absolutePath)); } void TestDir::createFiles(const QStringList& files) { - foreach(const QString& path, files) { + foreach (const QString& path, files) { createFile(path); } } void TestDir::createDir(const QString& path, const QDateTime& time) { QString absolutePath = path; makePathAbsoluteAndCreateParents(absolutePath); QDir(name()).mkdir(absolutePath); if (time.isValid()) { setTimeStamp(absolutePath, time); } Q_ASSERT(QFile::exists(absolutePath)); } void TestDir::makePathAbsoluteAndCreateParents(QString& path) { QFileInfo fileInfo(path); if (!fileInfo.isAbsolute()) { path = name() + path; fileInfo.setFile(path); } const QDir dir = fileInfo.dir(); if (!dir.exists()) { createDir(dir.absolutePath()); } Q_ASSERT(dir.exists()); } 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 @@ -1,59 +1,57 @@ /***************************************************************************** * Copyright (C) 2010-2011 by Frank Reininghaus (frank78ac@googlemail.com) * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef TESTDIR_H #define TESTDIR_H #include #include #include /** * 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); }; #endif 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 @@ -1,139 +1,132 @@ /*************************************************************************** * Copyright (C) 2010 by Peter Penz * * * * 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 "additionalinfoaccessor.h" -#include "dolphinmodel.h" #include #include class AdditionalInfoAccessorSingleton { public: AdditionalInfoAccessor instance; }; K_GLOBAL_STATIC(AdditionalInfoAccessorSingleton, s_additionalInfoManager) AdditionalInfoAccessor& AdditionalInfoAccessor::instance() { return s_additionalInfoManager->instance; } -KFileItemDelegate::InformationList AdditionalInfoAccessor::keys() const +QList 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; switch (type) { case SortByType: name = QLatin1String("sort_by_") + QLatin1String(m_map[info]->actionCollectionName); break; case AdditionalInfoType: name = QLatin1String("show_") + QLatin1String(m_map[info]->actionCollectionName); break; } 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]); - - // 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); + 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_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 @@ -1,109 +1,98 @@ /*************************************************************************** * Copyright (C) 2010 by Peter Penz * * * * 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 ADDITIONALINFOACCESSOR_H #define ADDITIONALINFOACCESSOR_H #include #include #include #include #include /** * @brief Allows to access the information that is available by KFileItemDelegate::Information. * * The information that is available by KFileItemDelegate::Information will be shown * in Dolphin the following way: * - As additional columns in the details view * - As additional lines in the icons view * - As menu entries in the "Sort By" and "Additional Information" groups * - As popup menu entries in the details view header popup * - As checkable entries in the View Properties dialog * * The AdditionalInfoAccessor provides a central place to get all available keys, * the corresponding translations, action names, etc., so that modifications or * extensions in KFileItemDelegate only require adjustments in the implementation * of this class. */ class LIBDOLPHINPRIVATE_EXPORT AdditionalInfoAccessor { public: enum ActionCollectionType { /// Action collection from "View -> Sort By" SortByType, /// Action collection from "View -> Additional Information" AdditionalInfoType }; static AdditionalInfoAccessor& instance(); /** * @return List of all available information entries of KFileItemDelegate. * All entries of this list are keys for accessing the corresponding * data (see actionCollectionName(), translation(), bitValue()). */ - KFileItemDelegate::InformationList keys() const; + QList 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(); virtual ~AdditionalInfoAccessor(); friend class AdditionalInfoAccessorSingleton; private: struct AdditionalInfo { const char* const actionCollectionName; const char* const context; 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 m_map; + QList m_infoList; + QMap 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 - * - * 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 - -#include -#include -#include -#include -#include - -#ifdef HAVE_NEPOMUK -#include -#endif - -#include -#include -#include -#include -#include - -#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(index.model()); - const DolphinModel *model = static_cast(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 - * - * 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 - -#include -#include - -#include - -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 * - * * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -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(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(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 * - * * - * 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 -#include -#include - -#include - -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 * - * * - * 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 -#include -#include -#include - -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::iterator start = m_columns.begin() + columnIndex; - QList::iterator end = m_columns.end(); - for (QList::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::iterator start = m_columns.begin() + 1; - QList::iterator end = m_columns.end(); - for (QList::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 * - * * - * 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 - -#include -#include -#include - -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 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 (peter.penz@gmx.at) * - * Copyright (C) 2008 by Simon St. James (kdedevel@etotheipiplusone.com) * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ - -#include "dolphindetailsview.h" - -#include "additionalinfoaccessor.h" -#include "dolphinmodel.h" -#include "dolphinviewcontroller.h" -#include "dolphinfileitemdelegate.h" -#include "settings/dolphinsettings.h" -#include "dolphinsortfilterproxymodel.h" -#include "dolphinviewautoscroller.h" -#include "draganddrophelper.h" -#include "viewextensionsfactory.h" -#include "viewmodecontroller.h" -#include "viewproperties.h" -#include "zoomlevelinfo.h" - -#include "dolphin_detailsmodesettings.h" -#include "dolphin_generalsettings.h" - -#include -#include -#include -#include - -#include -#include -#include - -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(proxyModel->sourceModel())->dirLister(); - connect(dirLister, SIGNAL(newItems(KFileItemList)), this, SLOT(resizeColumns())); -} - -DolphinDetailsView::~DolphinDetailsView() -{ -} - -QSet 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() << 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 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(model()); - const KDirModel* dirModel = qobject_cast(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 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 - // 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 * - * * - * 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 -#include -#include - -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 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 because a QModelIndex is not guaranteed to remain valid over time. - // Also a QSet 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 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 (frank78ac@googlemail.com) * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ - -#include "dolphindetailsviewexpander.h" - -#include "dolphindetailsview.h" -#include "dolphinmodel.h" -#include "dolphinsortfilterproxymodel.h" - -#include -#include - -DolphinDetailsViewExpander::DolphinDetailsViewExpander(DolphinDetailsView* parent, - const QSet& urlsToExpand) : - QObject(parent), - m_detailsView(parent), - m_dirLister(0), - m_dolphinModel(0), - m_proxyModel(0) -{ - Q_ASSERT(parent); - - m_proxyModel = qobject_cast(parent->model()); - Q_ASSERT(m_proxyModel); - - m_dolphinModel = qobject_cast(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 (frank78ac@googlemail.com) * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ - -#ifndef DOLPHINDETAILSVIEWEXPANDER_H -#define DOLPHINDETAILSVIEWEXPANDER_H - -#include -#include -#include - -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& 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 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 @@ -1,48 +1,48 @@ /*************************************************************************** * Copyright (C) 2006-2010 by Peter Penz * * * * 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 "dolphindirlister.h" #include #include -DolphinDirLister::DolphinDirLister() : - KDirLister() +DolphinDirLister::DolphinDirLister(QObject* parent) : + KDirLister(parent) { setAutoErrorHandlingEnabled(false, 0); } DolphinDirLister::~DolphinDirLister() { } void DolphinDirLister::handleError(KIO::Job* job) { if (job->error() == KIO::ERR_IS_FILE) { emit urlIsFileError(url()); } else { const QString errorString = job->errorString(); if (errorString.isEmpty()) { emit errorMessage(i18nc("@info:status", "Unknown error.")); } else { emit errorMessage(errorString); } } } #include "dolphindirlister.moc" 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 @@ -1,49 +1,49 @@ /*************************************************************************** * Copyright (C) 2006-2009 by Peter Penz * * * * 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 DOLPHINDIRLISTER_H #define DOLPHINDIRLISTER_H #include #include /** * @brief Extends the class KDirLister by emitting a signal when an * error occurred instead of showing an error dialog. */ class LIBDOLPHINPRIVATE_EXPORT DolphinDirLister : public KDirLister { Q_OBJECT public: - DolphinDirLister(); + DolphinDirLister(QObject* parent = 0); virtual ~DolphinDirLister(); signals: /** Is emitted whenever an error has occurred. */ void errorMessage(const QString& msg); /** Is emitted when the URL of the directory lister represents a file. */ void urlIsFileError(const KUrl& url); protected: virtual void handleError(KIO::Job* job); }; #endif 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 * - * * - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -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(index.model()); - const DolphinModel* dolphinModel = static_cast(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(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 * - * * - * 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 -#include - -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 * - * * - * 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 -#include -#include - -#include -#include -#include - -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(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(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(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(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 * - * * - * 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 - -#include -#include - -#include -#include -#include - -#include - -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 * + * * + * 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 +#include +#include +#include +#include + +#include +#include + +#include + +#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& roles) +{ + m_fileItemListView->setVisibleRoles(roles); + updateGridSize(); +} + +QHash 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 * + * * + * 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 +#include + +#include + +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& roles); + QHash 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 - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -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(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(index.model()); - const KFileItem item = dirModel->itemForIndex(index); - return item.linkDest(); - } - - case DolphinModel::LocalPathOrUrl: - const KDirModel *dirModel = qobject_cast(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(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(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 - * - * 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 -#include -#include - -#include -#include - -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 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 * - * Copyright (C) 2006 by Dominic Battre * - * Copyright (C) 2006 by Martin Pool * - * Copyright (C) 2007 by Rafael Fernández López * - * * - * 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(m_sorting), m_sortOrder); -} - -void DolphinSortFilterProxyModel::setSortOrder(Qt::SortOrder sortOrder) -{ - m_sortOrder = sortOrder; - KDirSortFilterProxyModel::sort(static_cast(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(m_sorting), tmpSortOrder); - - // Now comes the real sorting with the old column and sort order - KDirSortFilterProxyModel::sort(static_cast(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(m_sorting), sortOrder); -} - -DolphinView::Sorting DolphinSortFilterProxyModel::sortingForColumn(int column) -{ - Q_ASSERT(column >= 0); - Q_ASSERT(column <= DolphinView::MaxSortingEnum); - return static_cast(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 * - * * - * 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 -#include -#include - -/** - * @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 * - * Copyright (C) 2008 by Simon St. James * - * * - * 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 -#include -#include -#include -#include -#include - -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 * - * Copyright (C) 2008 by Simon St. James * - * * - * 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 -#include - -/** - * @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 @@ -1,1637 +1,1283 @@ /*************************************************************************** * Copyright (C) 2006-2009 by Peter Penz * * Copyright (C) 2006 by Gregor Kališnik * * * * 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 "dolphinview.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include +#include +#include #include #include #include #include #include #include #include #include #include #include #include #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(), m_createdItemUrl(), m_selectedItems(), m_newFileNames() { m_topLayout = new QVBoxLayout(this); 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&)), this, SLOT(openContextMenu(const QPoint&, const QList&))); connect(m_dolphinViewController, SIGNAL(urlsDropped(const KFileItem&, const KUrl&, QDropEvent*)), this, SLOT(dropUrls(const KFileItem&, const KUrl&, QDropEvent*))); connect(m_dolphinViewController, SIGNAL(sortingChanged(DolphinView::Sorting)), this, SLOT(updateSorting(DolphinView::Sorting))); connect(m_dolphinViewController, SIGNAL(sortOrderChanged(Qt::SortOrder)), 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&)), + this, SLOT(updateAdditionalInfo(const QList&)));*/ + //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 // creation is done asynchronously, several signals must be checked: connect(&DolphinNewFileMenuObserver::instance(), SIGNAL(itemCreated(const KUrl&)), this, SLOT(observeCreatedItem(const KUrl&))); m_selectionChangedTimer = new QTimer(this); m_selectionChangedTimer->setSingleShot(true); m_selectionChangedTimer->setInterval(300); 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>&)), + 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 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() { } 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) { if (active == m_active) { return; } m_active = active; QColor color = KColorScheme(QPalette::Active, KColorScheme::View).background().color(); if (!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 { return m_active; } 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 { 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; } const QItemSelection selection = m_viewAccessor.proxyModel()->mapSelectionToSource(view->selectionModel()->selection()); const QModelIndexList indexList = selection.indexes(); foreach (const QModelIndex &index, indexList) { KFileItem item = m_viewAccessor.dirModel()->itemForIndex(index); if (!item.isNull()) { itemList.append(item); } } - 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& urls) { foreach (const KUrl& url, urls) { KFileItem item(KFileItem::Unknown, KFileItem::Unknown, url); m_selectedItems.append(item); } } 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) { if (sorting != this->sorting()) { updateSorting(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) { if (sortOrder() != order) { updateSortOrder(order); } } Qt::SortOrder DolphinView::sortOrder() const { - return m_viewAccessor.proxyModel()->sortOrder(); + return Qt::AscendingOrder; // m_viewAccessor.proxyModel()->sortOrder(); } void DolphinView::setSortFoldersFirst(bool foldersFirst) { if (sortFoldersFirst() != foldersFirst) { updateSortFoldersFirst(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& info) { - const KUrl viewPropsUrl = rootUrl(); - ViewProperties props(viewPropsUrl); - props.setAdditionalInfo(info); - m_viewAccessor.itemDelegate()->setShowInformation(info); + const QList 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::additionalInfoList() const { - return m_viewAccessor.itemDelegate()->showInformation(); + return m_additionalInfoList; } void DolphinView::reload() { QByteArray viewState; QDataStream saveStream(&viewState, QIODevice::WriteOnly); saveState(saveStream); m_selectedItems= selectedItems(); setUrl(url()); loadDirectory(url(), true); QDataStream restoreStream(viewState); restoreState(restoreStream); } 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(); setActive(oldActivationState); updateZoomLevel(oldZoomLevel); } 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 { ++fileCount; totalFileSize += item.size(); } } } QString DolphinView::statusBarText() const { QString text; int folderCount = 0; int fileCount = 0; KIO::filesize_t totalFileSize = 0; if (hasSelection()) { // give a summary of the status of the selected files const KFileItemList list = selectedItems(); if (list.isEmpty()) { // when an item is triggered, it is temporary selected but selectedItems() // will return an empty list return text; } KFileItemList::const_iterator it = list.begin(); const KFileItemList::const_iterator end = list.end(); while (it != end) { const KFileItem& item = *it; if (item.isDir()) { ++folderCount; } else { ++fileCount; totalFileSize += item.size(); } ++it; } if (folderCount + fileCount == 1) { // if only one item is selected, show the filename const QString name = list.first().text(); text = (folderCount == 1) ? i18nc("@info:status", "%1 selected", name) : i18nc("@info:status", "%1 selected (%2)", name, KIO::convertSize(totalFileSize)); } else { // at least 2 items are selected const QString foldersText = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount); const QString filesText = i18ncp("@info:status", "1 File selected", "%1 Files selected", fileCount); if ((folderCount > 0) && (fileCount > 0)) { text = i18nc("@info:status folders, files (size)", "%1, %2 (%3)", foldersText, filesText, KIO::convertSize(totalFileSize)); } else if (fileCount > 0) { text = i18nc("@info:status files (size)", "%1 (%2)", filesText, KIO::convertSize(totalFileSize)); } else { Q_ASSERT(folderCount > 0); text = foldersText; } } } else { calculateItemCount(fileCount, folderCount, totalFileSize); text = KIO::itemsSummaryString(fileCount + folderCount, fileCount, folderCount, totalFileSize, true); } return text; } QList DolphinView::versionControlActions(const KFileItemList& items) const { - return m_dolphinViewController->versionControlActions(items); + Q_UNUSED(items); + return QList(); //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(); } } 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. const KFileItemList selItems = selectedItems(); clearSelection(); QItemSelection invertedSelection; foreach (const KFileItem& item, items()) { if (!selItems.contains(item)) { const QModelIndex index = m_viewAccessor.proxyModel()->mapFromSource(m_viewAccessor.dirModel()->indexForItem(item)); invertedSelection.select(index, index); } } 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() { KFileItemList items = selectedItems(); const int itemCount = items.count(); if (itemCount < 1) { 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 m_assureVisibleCurrentIndex = true; } void DolphinView::trashSelectedItems() { const KUrl::List list = simplifiedSelectedUrls(); KonqOperations::del(this, KonqOperations::TRASH, list); } void DolphinView::deleteSelectedItems() { const KUrl::List list = simplifiedSelectedUrls(); const bool del = KonqOperations::askDeleteConfirmation(list, KonqOperations::DEL, KonqOperations::DEFAULT_CONFIRMATION, this); if (del) { KIO::Job* job = KIO::del(list); connect(job, SIGNAL(result(KJob*)), this, SLOT(slotDeleteFileFinished(KJob*))); } } void DolphinView::cutSelectedItems() { QMimeData* mimeData = selectionMimeData(); KonqMimeData::addIsCutSelection(mimeData, true); QApplication::clipboard()->setMimeData(mimeData); } void DolphinView::copySelectedItems() { QMimeData* mimeData = selectionMimeData(); QApplication::clipboard()->setMimeData(mimeData); } void DolphinView::paste() { pasteToUrl(url()); } void DolphinView::pasteIntoFolder() { const KFileItemList items = selectedItems(); if ((items.count() == 1) && items.first().isDir()) { pasteToUrl(items.first().url()); } } -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) { if (categorized == categorizedSorting()) { 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) { QWidget::mouseReleaseEvent(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(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(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; - - default: - break; - } - - return QWidget::eventFilter(watched, event); -} + Q_UNUSED(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()); } } 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()); } +} - 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) { const int count = selectedItemsCount(); const bool selectionStateChanged = ((count > 0) && (selected.count() == count)) || ((count == 0) && !deselected.isEmpty()); // If nothing has been selected before and something got selected (or if something // was selected before and now nothing is selected) the selectionChangedSignal must // be emitted asynchronously as fast as possible to update the edit-actions. m_selectionChangedTimer->setInterval(selectionStateChanged ? 0 : 300); m_selectionChangedTimer->start(); } void DolphinView::emitSelectionChangedSignal() { m_selectionChangedTimer->stop(); emit selectionChanged(selectedItems()); } void DolphinView::openContextMenu(const QPoint& pos, const QList& 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 DolphinView::pasteInfo() const { return KonqOperations::pasteInfo(url()); } void DolphinView::setTabsForFilesEnabled(bool tabsForFiles) { m_tabsForFiles = tabsForFiles; } bool DolphinView::isTabsForFilesEnabled() const { return m_tabsForFiles; } bool DolphinView::itemsExpandable() const { - return m_viewAccessor.itemsExpandable(); + return false; //m_viewAccessor.itemsExpandable(); } 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 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(); } 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 } } void DolphinView::restoreContentsPosition() { if (!m_restoredContentsPosition.isNull()) { const int x = m_restoredContentsPosition.x(); 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) { emit requestItemInfo(item); } void DolphinView::clearHoverInformation() { emit requestItemInfo(KFileItem()); } void DolphinView::slotDeleteFileFinished(KJob* job) { if (job->error() == 0) { emit operationCompletedMessage(i18nc("@info:status", "Delete operation completed.")); } else if (job->error() != KIO::ERR_USER_CANCELED) { emit errorMessage(job->errorString()); } } void DolphinView::slotDirListerStarted(const KUrl& url) { // Disable the writestate temporary until it can be determined in a fast way // in DolphinView::slotDirListerCompleted() if (m_isFolderWritable) { m_isFolderWritable = false; emit writeStateChanged(m_isFolderWritable); } emit startedPathLoading(url); } void DolphinView::slotDirListerCompleted() { if (!m_expanderActive) { slotLoadingCompleted(); } 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(); } updateWritableState(); } void DolphinView::slotLoadingCompleted() { m_expanderActive = false; 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) { url = item.url().upUrl(); if (baseUrl.equals(url, KUrl::CompareWithoutTrailingSlash)) { const QModelIndex index = m_viewAccessor.proxyModel()->mapFromSource(m_viewAccessor.dirModel()->indexForItem(item)); 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()); } 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(m_container->controller()->model()); +} + void DolphinView::loadDirectory(const KUrl& url, bool reload) { if (!url.isValid()) { const QString location(url.pathOrUrl()); if (location.isEmpty()) { emit errorMessage(i18nc("@info:status", "The location is empty.")); } else { emit errorMessage(i18nc("@info:status", "The location '%1' is invalid.", location)); } 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); emit sortOrderChanged(sortOrder); } const bool sortFoldersFirst = props.sortFoldersFirst(); if (sortFoldersFirst != m_viewAccessor.proxyModel()->sortFoldersFirst()) { 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 infoList = props.additionalInfoList(); + if (infoList != m_additionalInfoList) { + const QList 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 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) { addNewFileNames(QApplication::clipboard()->mimeData()); KonqOperations::doPaste(this, 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()); } 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) { const KUrl::List urls = KUrl::List::fromMimeData(mimeData); foreach (const KUrl& url, urls) { m_newFileNames.insert(url.fileName()); } } 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); for (int row = 0; row < rowCount; ++row) { QModelIndex index = proxyModel->index(row, 0, parent); QModelIndex sourceIndex = proxyModel->mapToSource(index); if (sourceIndex.isValid() && pattern.exactMatch(dolphinModel->data(sourceIndex).toString())) { matchingIndexes += QItemSelectionRange(index); } 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>&)), - 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>&)), - 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(); } if (m_isFolderWritable != wasFolderWritable) { emit writeStateChanged(m_isFolderWritable); } } -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(itemView()->itemDelegate()); -} - -QWidget* DolphinView::ViewAccessor::layoutTarget() const +QByteArray DolphinView::sortRoleForSorting(Sorting sorting) const { - if (m_columnsContainer) { - return m_columnsContainer; + 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 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 DolphinView::ViewAccessor::expandedUrls() const -{ - if (m_detailsView) { - return m_detailsView->expandedUrls(); - } - - return QSet(); -} - -const DolphinDetailsViewExpander* DolphinView::ViewAccessor::setExpandedUrls(const QSet& 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 -{ - // 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(proxyModel()->sourceModel()); -} - -DolphinSortFilterProxyModel* DolphinView::ViewAccessor::proxyModel() const -{ - if (m_columnsContainer) { - return static_cast(m_columnsContainer->activeColumn()->model()); - } - 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 @@ -1,876 +1,779 @@ /*************************************************************************** * Copyright (C) 2006-2009 by Peter Penz * * Copyright (C) 2006 by Gregor Kališnik * * * * 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 DOLPHINVIEW_H #define DOLPHINVIEW_H #include #include "libdolphin_export.h" #include #include #include #include #include #include #include #include #include #include #include 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 { Q_OBJECT 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. */ enum Sorting { SortByName = 0, SortBySize, SortByDate, SortByPermissions, SortByOwner, 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(); /** * Returns the current active URL, where all actions are applied. * The URL navigator is synchronized with this URL. */ 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. */ void setActive(bool active); bool isActive() const; /** * Changes the view mode for the current directory to \a mode. * If the view properties should be remembered for each directory * (GeneralSettings::globalViewProps() returns false), then the * changed view mode will be stored automatically. */ 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; /** * Returns the selected items. The list is empty if no item has been * selected. */ KFileItemList selectedItems() const; /** * Returns the number of selected items (this is faster than * invoking selectedItems().count()). */ int selectedItemsCount() const; /** * Marks the items indicated by \p urls to get selected after the * directory DolphinView::url() has been loaded. Note that nothing * gets selected if no loading of a directory has been triggered * by DolphinView::setUrl() or DolphinView::reload(). */ void markUrlsAsSelected(const QList& urls); /** * All items that match to the pattern \a pattern will get selected * if \a enabled is true and deselected if \a enabled is false. */ void setItemSelectionEnabled(const QRegExp& pattern, bool enabled); /** * Sets the zoom level to \a level. It is assured that the used * level is adjusted to be inside the range ZoomLevelInfo::minimumLevel() and * ZoomLevelInfo::maximumLevel(). */ void setZoomLevel(int level); int zoomLevel() const; /** * Returns true, if zooming in is possible. If false is returned, * the maximum zooming level has been reached. */ bool isZoomInPossible() const; /** * Returns true, if zooming out is possible. If false is returned, * the minimum zooming level has been reached. */ bool isZoomOutPossible() const; /** Sets the sort order of the items inside a directory (see DolphinView::Sorting). */ void setSorting(Sorting sorting); /** Returns the sort order of the items inside a directory (see DolphinView::Sorting). */ Sorting sorting() const; /** Sets the sort order (Qt::Ascending or Qt::Descending) for the items. */ void setSortOrder(Qt::SortOrder order); /** Returns the current used sort order (Qt::Ascending or Qt::Descending). */ Qt::SortOrder sortOrder() const; /** Sets a separate sorting with folders first (true) or a mixed sorting of files and folders (false). */ void setSortFoldersFirst(bool foldersFirst); /** Returns if files and folders are sorted separately or not. */ bool sortFoldersFirst() const; /** Sets the additional information which should be shown for the items. */ - void setAdditionalInfo(KFileItemDelegate::InformationList info); + void setAdditionalInfoList(const QList& info); /** Returns the additional information which should be shown for the items. */ - KFileItemDelegate::InformationList additionalInfo() const; + QList additionalInfoList() const; /** Reloads the current directory. */ void reload(); void stopLoading(); /** * Refreshes the view to get synchronized with the (updated) Dolphin settings. * This method only needs to get invoked if the view settings for the Icons View, * Details View or Columns View have been changed. */ void refresh(); /** * Filters the currently shown items by \a nameFilter. All items * which contain the given filter string will be shown. */ void setNameFilter(const QString& nameFilter); QString nameFilter() const; /** * Calculates the number of currently shown files into * \a fileCount and the number of folders into \a folderCount. * The size of all files is written into \a totalFileSize. * It is recommend using this method instead of asking the * directory lister or the model directly, as it takes * filtering and hierarchical previews into account. */ void calculateItemCount(int& fileCount, int& folderCount, KIO::filesize_t& totalFileSize) const; /** * Returns a textual representation of the state of the current * folder or selected items, suitable for use in the status bar. */ QString statusBarText() const; /** * Returns the version control actions that are provided for the items \p items. * Usually the actions are presented in the context menu. */ QList 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 */ QPair pasteInfo() const; /** * If \a tabsForFiles is true, the signal tabRequested() will also * emitted also for files. Per default tabs for files is disabled * and hence the signal tabRequested() will only be emitted for * directories. */ void setTabsForFilesEnabled(bool tabsForFiles); bool isTabsForFilesEnabled() const; /** * Returns true if the current view allows folders to be expanded, * i.e. presents a hierarchical view to the user. */ bool itemsExpandable() const; /** * Restores the view state (current item, contents position, details view expansion state) */ void restoreState(QDataStream& stream); /** * Saves the view state (current item, contents position, details view expansion state) */ void saveState(QDataStream& stream); /** 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 * \a url, nothing will be done (use DolphinView::reload() instead). */ void setUrl(const KUrl& url); /** * Selects all items. * @see DolphinView::selectedItems() */ void selectAll(); /** * Inverts the current selection: selected items get unselected, * unselected items get selected. * @see DolphinView::selectedItems() */ void invertSelection(); void clearSelection(); /** * Triggers the renaming of the currently selected items, where * the user must input a new name for the items. */ void renameSelectedItems(); /** * Moves all selected items to the trash. */ void trashSelectedItems(); /** * Deletes all selected items. */ void deleteSelectedItems(); /** * Copies all selected items to the clipboard and marks * the items as cut. */ void cutSelectedItems(); /** Copies all selected items to the clipboard. */ void copySelectedItems(); /** Pastes the clipboard data to this view. */ void paste(); /** * Pastes the clipboard data into the currently selected * folder. If the current selection is not exactly one folder, no * paste operation is done. */ void pasteIntoFolder(); /** * Turns on the file preview for the all files of the current directory, * if \a show is true. * If the view properties should be remembered for each directory * (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, * if \a show is true. * If the view properties should be remembered for each directory * (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 * is true. * If the view properties should be remembered for each directory * (GeneralSettings::globalViewProps() returns false), then the * categorized sorting setting will be stored automatically. */ void setCategorizedSorting(bool categorized); signals: /** * Is emitted if the view has been activated by e. g. a mouse click. */ 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); /** * Is emitted when clicking on an item with the left mouse button. */ void itemTriggered(const KFileItem& item); /** * Is emitted if items have been added or deleted. */ void itemCountChanged(); /** * Is emitted if a new tab should be opened for the URL \a url. */ void tabRequested(const KUrl& url); /** * 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); /** Is emitted if the sort order (ascending or descending) has been changed. */ void sortOrderChanged(Qt::SortOrder order); /** Is emitted if the sorting of files and folders (separate with folders first or mixed) has been changed. */ void sortFoldersFirstChanged(bool foldersFirst); /** Is emitted if the additional information shown for this view has been changed. */ - void additionalInfoChanged(); + void additionalInfoListChanged(const QList& current, + const QList& 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. * If item is null, no item information request is pending. */ void requestItemInfo(const KFileItem& item); /** * Is emitted whenever the selection has been changed. */ void selectionChanged(const KFileItemList& selection); /** * Is emitted if a context menu is requested for the item \a item, * which is part of \a url. If the item is null, the context menu * for the URL should be shown and the custom actions \a customActions * will be added. */ void requestContextMenu(const KFileItem& item, const KUrl& url, const QList& customActions); /** * Is emitted if an information message with the content \a msg * should be shown. */ void infoMessage(const QString& msg); /** * Is emitted if an error message with the content \a msg * should be shown. */ void errorMessage(const QString& msg); /** * Is emitted if an "operation completed" message with the content \a msg * should be shown. */ void operationCompletedMessage(const QString& msg); /** * Is emitted after DolphinView::setUrl() has been invoked and * the path \a url is currently loaded. If this signal is emitted, * it is assured that the view contains already the correct root * URL and property settings. */ void startedPathLoading(const KUrl& url); /** * Is emitted after the path triggered by DolphinView::setUrl() * has been loaded. */ void finishedPathLoading(const KUrl& url); /** * Is emitted after DolphinView::setUrl() has been invoked and provides * the information how much percent of the current path have been loaded. */ void pathLoadingProgress(int percent); /** * Is emitted if the DolphinView::setUrl() is invoked but the URL is not * a directory. */ void urlIsFileError(const KUrl& file); /** * Emitted when KDirLister emits redirection. * Testcase: fish://localhost */ void redirection(const KUrl& oldUrl, const KUrl& newUrl); /** * Is emitted when the write state of the folder has been changed. The application * should disable all actions like "Create New..." that depend on the write * state. */ 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: /** * Marks the view as active (DolphinView:isActive() will return true) * and emits the 'activated' signal if it is not already active. */ 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 * because getting all file items for the signal can be an expensive * operation. Fast selection changes are collected in this case and * the signal is emitted only after no selection change has been done * within a small delay. */ void slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); /** * Is called by emitDelayedSelectionChangedSignal() and emits the * signal \a selectionChanged() with all selected file items as parameter. */ void emitSelectionChangedSignal(); /** * Opens the context menu on position \a pos. The position * is used to check whether the context menu is related to an * item or to the viewport. */ void openContextMenu(const QPoint& pos, const QList& customActions); /** * Drops dragged URLs to the destination path \a destPath. If * the URLs are dropped above an item inside the destination path, * the item is indicated by \a destItem. */ void dropUrls(const KFileItem& destItem, const KUrl& destPath, QDropEvent* event); /** * Updates the view properties of the current URL to the * sorting given by \a sorting. */ void updateSorting(DolphinView::Sorting sorting); /** * Updates the view properties of the current URL to the * sort order given by \a order. */ void updateSortOrder(Qt::SortOrder order); /** * Updates the view properties of the current URL to the * sorting of files and folders (separate with folders first or mixed) given by \a foldersFirst. */ 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. * @see DolphinView::clearHoverInformation() */ void showHoverInformation(const KFileItem& item); /** * Clears the hover information shown in the status bar. * @see DolphinView::showHoverInformation(). */ void clearHoverInformation(); /** * Indicates in the status bar that the delete operation * of the job \a job has been finished. */ void slotDeleteFileFinished(KJob* job); /** * Invoked when the directory lister has been started the * loading of \a url. */ void slotDirListerStarted(const KUrl& url); /** * Invoked when the directory lister has completed the loading of * items. Assures that pasted items and renamed items get seleced. */ void slotDirListerCompleted(); /** * Invoked when the loading of the directory is finished. * Restores the active item and the scroll position if possible. */ void slotLoadingCompleted(); /** * Is invoked when the KDirLister indicates refreshed items. */ void slotRefreshItems(); /** * Observes the item with the URL \a url. As soon as the directory * model indicates that the item is available, the item will * get selected and it is assure that the item stays visible. * * @see selectAndScrollToCreatedItem() */ void observeCreatedItem(const KUrl& url); /** * Selects and scrolls to the item that got observed * by observeCreatedItem(). */ void selectAndScrollToCreatedItem(); /** * Called when a redirection happens. * Testcase: fish://localhost */ void slotRedirection(const KUrl& oldUrl, const KUrl& newUrl); /** * Restores the contents position, if history information about the old position is available. */ void restoreContentsPosition(); - void slotUrlChangeRequested(const KUrl& url); + //void slotUrlChangeRequested(const KUrl& url); private: + KFileItemModel* fileItemModel() const; + void loadDirectory(const KUrl& url, bool reload = false); /** * Applies the view properties which are defined by the current URL * to the DolphinView properties. */ 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(). * Pastes the clipboard data into the URL \a url. */ void pasteToUrl(const KUrl& url); /** * Checks whether the current item view has the same zoom level * as \a oldZoomLevel. If this is not the case, the zoom level * of the controller is updated and a zoomLevelChanged() signal * is emitted. */ void updateZoomLevel(int oldZoomLevel); /** * Returns a list of URLs for all selected items. The list is * simplified, so that when the URLs are part of different tree * levels, only the parent is returned. */ KUrl::List simplifiedSelectedUrls() const; /** * Returns the MIME data for all selected items. */ QMimeData* selectionMimeData() const; /** * Is invoked after a paste operation or a drag & drop * operation and adds the filenames of all URLs from \a mimeData to * m_newFileNames. This allows to select all newly added * items in slotDirListerCompleted(). */ void addNewFileNames(const QMimeData* mimeData); /** * Helper method for DolphinView::setItemSelectionEnabled(): Returns the selection for * all items of \p parent that match with the regular expression defined by \p pattern. */ 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 * writeableStateChanged() will be emitted. */ 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 expandedUrls() const; - const DolphinDetailsViewExpander* setExpandedUrls(const QSet& 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 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 m_additionalInfoList; QVBoxLayout* m_topLayout; - DolphinViewController* m_dolphinViewController; - ViewModeController* m_viewModeController; - ViewAccessor m_viewAccessor; + DolphinDirLister* m_dirLister; + DolphinItemListContainer* m_container; QTimer* m_selectionChangedTimer; KUrl m_activeItemUrl; QPoint m_restoredContentsPosition; KUrl m_createdItemUrl; // URL for a new item that got created by the "Create New..." menu KFileItemList m_selectedItems; // this is used for making the View to remember selections after F5 /** * Remembers the filenames that have been added by a paste operation * or a drag & drop operation. Allows to select the items in * slotDirListerCompleted(). */ QSet m_newFileNames; // For unit tests friend class TestBase; friend class DolphinDetailsViewTest; }; /// Allow using DolphinView::Mode in QVariant Q_DECLARE_METATYPE(DolphinView::Mode) #endif // DOLPHINVIEW_H 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 @@ -1,537 +1,557 @@ /*************************************************************************** * Copyright (C) 2008 by David Faure * * * * 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 "dolphinviewactionhandler.h" #include "additionalinfoaccessor.h" #include "settings/viewpropertiesdialog.h" #include "views/dolphinview.h" #include "views/zoomlevelinfo.h" #include #include #include #include #include #include #include #include #include #include #include +#include + DolphinViewActionHandler::DolphinViewActionHandler(KActionCollection* collection, QObject* parent) : QObject(parent), m_actionCollection(collection), m_currentView(0) { Q_ASSERT(m_actionCollection); createActions(); } 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, + QList)), + this, SLOT(slotAdditionalInfoListChanged(QList, + QList))); + 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() { return m_currentView; } void DolphinViewActionHandler::createActions() { // This action doesn't appear in the GUI, it's for the shortcut only. // KNewFileMenu takes care of the GUI stuff. KAction* newDirAction = m_actionCollection->addAction("create_dir"); newDirAction->setText(i18nc("@action", "Create Folder...")); newDirAction->setShortcut(Qt::Key_F10); newDirAction->setIcon(KIcon("folder-new")); connect(newDirAction, SIGNAL(triggered()), this, SIGNAL(createDirectory())); // File menu KAction* rename = m_actionCollection->addAction("rename"); rename->setText(i18nc("@action:inmenu File", "Rename...")); rename->setShortcut(Qt::Key_F2); rename->setIcon(KIcon("edit-rename")); connect(rename, SIGNAL(triggered()), this, SLOT(slotRename())); KAction* moveToTrash = m_actionCollection->addAction("move_to_trash"); moveToTrash->setText(i18nc("@action:inmenu File", "Move to Trash")); moveToTrash->setIcon(KIcon("user-trash")); moveToTrash->setShortcut(QKeySequence::Delete); connect(moveToTrash, SIGNAL(triggered(Qt::MouseButtons, Qt::KeyboardModifiers)), this, SLOT(slotTrashActivated(Qt::MouseButtons, Qt::KeyboardModifiers))); KAction* deleteAction = m_actionCollection->addAction("delete"); deleteAction->setIcon(KIcon("edit-delete")); deleteAction->setText(i18nc("@action:inmenu File", "Delete")); deleteAction->setShortcut(Qt::SHIFT | Qt::Key_Delete); connect(deleteAction, SIGNAL(triggered()), this, SLOT(slotDeleteItems())); // This action is useful for being enabled when "move_to_trash" should be // disabled and "delete" is enabled (e.g. non-local files), so that Key_Del // can be used for deleting the file (#76016). It needs to be a separate action // so that the Edit menu isn't affected. KAction* deleteWithTrashShortcut = m_actionCollection->addAction("delete_shortcut"); // The descriptive text is just for the shortcuts editor. deleteWithTrashShortcut->setText(i18nc("@action \"Move to Trash\" for non-local files, etc.", "Delete (using shortcut for Trash)")); deleteWithTrashShortcut->setShortcut(QKeySequence::Delete); deleteWithTrashShortcut->setEnabled(false); connect(deleteWithTrashShortcut, SIGNAL(triggered()), this, SLOT(slotDeleteItems())); KAction *propertiesAction = m_actionCollection->addAction( "properties" ); // Well, it's the File menu in dolphinmainwindow and the Edit menu in dolphinpart... :) propertiesAction->setText( i18nc("@action:inmenu File", "Properties") ); propertiesAction->setIcon(KIcon("document-properties")); propertiesAction->setShortcut(Qt::ALT | Qt::Key_Return); connect(propertiesAction, SIGNAL(triggered()), SLOT(slotProperties())); // View menu KToggleAction* iconsAction = iconsModeAction(); + KToggleAction* compactAction = compactModeAction(); KToggleAction* detailsAction = detailsModeAction(); - KToggleAction* columnsAction = columnsModeAction(); KSelectAction* viewModeActions = m_actionCollection->add("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*))); KStandardAction::zoomIn(this, SLOT(zoomIn()), m_actionCollection); KStandardAction::zoomOut(this, SLOT(zoomOut()), m_actionCollection); KToggleAction* showPreview = m_actionCollection->add("show_preview"); showPreview->setText(i18nc("@action:intoolbar", "Preview")); showPreview->setToolTip(i18nc("@info", "Show preview of files and folders")); showPreview->setIcon(KIcon("view-preview")); connect(showPreview, SIGNAL(triggered(bool)), this, SLOT(togglePreview(bool))); KToggleAction* sortDescending = m_actionCollection->add("descending"); sortDescending->setText(i18nc("@action:inmenu Sort", "Descending")); connect(sortDescending, SIGNAL(triggered()), this, SLOT(toggleSortOrder())); KToggleAction* sortFoldersFirst = m_actionCollection->add("folders_first"); sortFoldersFirst->setText(i18nc("@action:inmenu Sort", "Folders First")); connect(sortFoldersFirst, SIGNAL(triggered()), this, SLOT(toggleSortFoldersFirst())); // View -> Sort By QActionGroup* sortByActionGroup = createSortByActionGroup(); connect(sortByActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotSortTriggered(QAction*))); KActionMenu* sortByActionMenu = m_actionCollection->add("sort"); sortByActionMenu->setText(i18nc("@action:inmenu View", "Sort By")); sortByActionMenu->setDelayed(false); foreach (QAction* action, sortByActionGroup->actions()) { sortByActionMenu->addAction(action); } sortByActionMenu->addSeparator(); sortByActionMenu->addAction(sortDescending); sortByActionMenu->addAction(sortFoldersFirst); // View -> Additional Information QActionGroup* additionalInfoGroup = createAdditionalInformationActionGroup(); connect(additionalInfoGroup, SIGNAL(triggered(QAction*)), this, SLOT(toggleAdditionalInfo(QAction*))); KActionMenu* additionalInfoMenu = m_actionCollection->add("additional_info"); additionalInfoMenu->setText(i18nc("@action:inmenu View", "Additional Information")); additionalInfoMenu->setDelayed(false); foreach (QAction* action, additionalInfoGroup->actions()) { additionalInfoMenu->addAction(action); } KToggleAction* showInGroups = m_actionCollection->add("show_in_groups"); showInGroups->setText(i18nc("@action:inmenu View", "Show in Groups")); connect(showInGroups, SIGNAL(triggered(bool)), this, SLOT(toggleSortCategorization(bool))); KToggleAction* showHiddenFiles = m_actionCollection->add("show_hidden_files"); showHiddenFiles->setText(i18nc("@action:inmenu View", "Show Hidden Files")); showHiddenFiles->setShortcuts(QList() << Qt::ALT + Qt::Key_Period << Qt::Key_F8); connect(showHiddenFiles, SIGNAL(triggered(bool)), this, SLOT(toggleShowHiddenFiles(bool))); KAction* adjustViewProps = m_actionCollection->addAction("view_properties"); adjustViewProps->setText(i18nc("@action:inmenu View", "Adjust View Properties...")); connect(adjustViewProps, SIGNAL(triggered()), this, SLOT(slotAdjustViewProperties())); } QActionGroup* DolphinViewActionHandler::createAdditionalInformationActionGroup() { QActionGroup* additionalInfoGroup = new QActionGroup(m_actionCollection); additionalInfoGroup->setExclusive(false); KActionMenu* showInformationMenu = m_actionCollection->add("additional_info"); showInformationMenu->setText(i18nc("@action:inmenu View", "Additional Information")); showInformationMenu->setDelayed(false); const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); - const KFileItemDelegate::InformationList infoKeys = infoAccessor.keys(); - foreach (KFileItemDelegate::Information info, infoKeys) { + const QList infoList = infoAccessor.keys(); + foreach (DolphinView::AdditionalInfo info, infoList) { const QString name = infoAccessor.actionCollectionName(info, AdditionalInfoAccessor::AdditionalInfoType); KToggleAction* action = m_actionCollection->add(name); action->setText(infoAccessor.translation(info)); action->setData(info); action->setActionGroup(additionalInfoGroup); } return additionalInfoGroup; } Q_DECLARE_METATYPE(DolphinView::Sorting) QActionGroup* DolphinViewActionHandler::createSortByActionGroup() { QActionGroup* sortByActionGroup = new QActionGroup(m_actionCollection); sortByActionGroup->setExclusive(true); KToggleAction* sortByName = m_actionCollection->add("sort_by_name"); sortByName->setText(i18nc("@action:inmenu Sort By", "Name")); sortByName->setData(QVariant::fromValue(DolphinView::SortByName)); sortByActionGroup->addAction(sortByName); const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); - const KFileItemDelegate::InformationList infoKeys = infoAccessor.keys(); - foreach (KFileItemDelegate::Information info, infoKeys) { + const QList infoList = infoAccessor.keys(); + foreach (DolphinView::AdditionalInfo info, infoList) { const QString name = infoAccessor.actionCollectionName(info, AdditionalInfoAccessor::SortByType); KToggleAction* action = m_actionCollection->add(name); action->setText(infoAccessor.translation(info)); const DolphinView::Sorting sorting = infoAccessor.sorting(info); action->setData(QVariant::fromValue(sorting)); sortByActionGroup->addAction(action); } return sortByActionGroup; } void DolphinViewActionHandler::slotViewModeActionTriggered(QAction* action) { const DolphinView::Mode mode = action->data().value(); m_currentView->setMode(mode); QAction* viewModeMenu = m_actionCollection->action("view_mode"); viewModeMenu->setIcon(KIcon(action->icon())); } void DolphinViewActionHandler::slotRename() { emit actionBeingHandled(); m_currentView->renameSelectedItems(); } void DolphinViewActionHandler::slotTrashActivated(Qt::MouseButtons, Qt::KeyboardModifiers modifiers) { emit actionBeingHandled(); // Note: kde3's konq_mainwindow.cpp used to check // reason == KAction::PopupMenuActivation && ... // but this isn't supported anymore if (modifiers & Qt::ShiftModifier) { m_currentView->deleteSelectedItems(); } else { m_currentView->trashSelectedItems(); } } void DolphinViewActionHandler::slotDeleteItems() { emit actionBeingHandled(); m_currentView->deleteSelectedItems(); } 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(); } QString DolphinViewActionHandler::currentViewModeActionName() const { switch (m_currentView->mode()) { case DolphinView::IconsView: return "icons"; case DolphinView::DetailsView: return "details"; - case DolphinView::ColumnView: - return "columns"; + case DolphinView::CompactView: + return "compact"; } return QString(); // can't happen } KActionCollection* DolphinViewActionHandler::actionCollection() { return m_actionCollection; } void DolphinViewActionHandler::updateViewActions() { QAction* viewModeAction = m_actionCollection->action(currentViewModeActionName()); if (viewModeAction) { viewModeAction->setChecked(true); QAction* viewModeMenu = m_actionCollection->action("view_mode"); viewModeMenu->setIcon(KIcon(viewModeAction->icon())); } 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()); + 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() { const int level = m_currentView->zoomLevel(); m_currentView->setZoomLevel(level + 1); updateViewActions(); } void DolphinViewActionHandler::zoomOut() { const int level = m_currentView->zoomLevel(); m_currentView->setZoomLevel(level - 1); updateViewActions(); } void DolphinViewActionHandler::toggleSortOrder() { const Qt::SortOrder order = (m_currentView->sortOrder() == Qt::AscendingOrder) ? Qt::DescendingOrder : Qt::AscendingOrder; m_currentView->setSortOrder(order); } void DolphinViewActionHandler::toggleSortFoldersFirst() { const bool sortFirst = m_currentView->sortFoldersFirst(); m_currentView->setSortFoldersFirst(!sortFirst); } void DolphinViewActionHandler::slotSortOrderChanged(Qt::SortOrder order) { QAction* descending = m_actionCollection->action("descending"); const bool sortDescending = (order == Qt::DescendingOrder); descending->setChecked(sortDescending); } void DolphinViewActionHandler::slotSortFoldersFirstChanged(bool foldersFirst) { m_actionCollection->action("folders_first")->setChecked(foldersFirst); } void DolphinViewActionHandler::toggleAdditionalInfo(QAction* action) { emit actionBeingHandled(); - const KFileItemDelegate::Information info = - static_cast(action->data().toInt()); + const DolphinView::AdditionalInfo info = + static_cast(action->data().toInt()); - KFileItemDelegate::InformationList list = m_currentView->additionalInfo(); + QList list = m_currentView->additionalInfoList(); const bool show = action->isChecked(); const int index = list.indexOf(info); 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& current, + const QList& previous) { - m_currentView->updateAdditionalInfoActions(m_actionCollection); + Q_UNUSED(previous); + + const AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); + + const QList checkedInfo = current; + const QList 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) { 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); } KToggleAction* DolphinViewActionHandler::iconsModeAction() { KToggleAction* iconsView = m_actionCollection->add("icons"); iconsView->setText(i18nc("@action:inmenu View Mode", "Icons")); iconsView->setToolTip(i18nc("@info", "Icons view mode")); iconsView->setShortcut(Qt::CTRL | Qt::Key_1); iconsView->setIcon(KIcon("view-list-icons")); iconsView->setData(QVariant::fromValue(DolphinView::IconsView)); return iconsView; } +KToggleAction* DolphinViewActionHandler::compactModeAction() +{ + KToggleAction* iconsView = m_actionCollection->add("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("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("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; if (sorting == DolphinView::SortByName) { 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 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); break; } } } if (action) { action->setChecked(true); QAction* sortByMenu = m_actionCollection->action("sort"); sortByMenu->setIcon(KIcon(action->icon())); } } -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()); } } void DolphinViewActionHandler::slotSortTriggered(QAction* action) { const DolphinView::Sorting sorting = action->data().value(); m_currentView->setSorting(sorting); } void DolphinViewActionHandler::slotAdjustViewProperties() { emit actionBeingHandled(); QPointer dialog = new ViewPropertiesDialog(m_currentView); dialog->exec(); delete dialog; } void DolphinViewActionHandler::slotProperties() { KPropertiesDialog* dialog = 0; const KFileItemList list = m_currentView->selectedItems(); if (list.isEmpty()) { const KUrl url = m_currentView->url(); dialog = new KPropertiesDialog(url, m_currentView); } else { dialog = new KPropertiesDialog(list, m_currentView); } dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); dialog->raise(); dialog->activateWindow(); } 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 @@ -1,252 +1,253 @@ /*************************************************************************** * Copyright (C) 2008 by David Faure * * * * 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 DOLPHINVIEWACTIONHANDLER_H #define DOLPHINVIEWACTIONHANDLER_H #include "libdolphin_export.h" #include #include #include "views/dolphinview.h" #include class KToggleAction; class QAction; class QActionGroup; class DolphinView; class KActionCollection; /** * @short Handles all actions for DolphinView * * The action handler owns all the actions and slots related to DolphinView, * but can the view that is acts upon can be switched to another one * (this is used in the case of split views). * * The purpose of this class is also to share this code between DolphinMainWindow * and DolphinPart. * * @see DolphinView * @see DolphinMainWindow * @see DolphinPart */ class LIBDOLPHINPRIVATE_EXPORT DolphinViewActionHandler : public QObject { Q_OBJECT public: explicit DolphinViewActionHandler(KActionCollection* collection, QObject* parent); /** * Sets the view that this action handler should work on. */ void setCurrentView(DolphinView* view); /** * Returns the view that this action handler should work on. */ DolphinView* currentView(); /** * Returns the name of the action for the current viewmode */ QString currentViewModeActionName() const; /** * Returns m_actionCollection */ KActionCollection* actionCollection(); public Q_SLOTS: /** * Update all actions in the 'View' menu, i.e. those that depend on the * settings in the current view. */ void updateViewActions(); Q_SIGNALS: /** * Emitted by DolphinViewActionHandler when the user triggered an action. * This is only used for clearining the statusbar in DolphinMainWindow. */ void actionBeingHandled(); /** * Emitted if the user requested creating a new directory by the F10 key. * The receiver of the signal (DolphinMainWindow or DolphinPart) invokes * the method createDirectory of their KNewFileMenu instance. */ void createDirectory(); private Q_SLOTS: /** * Emitted when the user requested a change of view mode */ void slotViewModeActionTriggered(QAction*); /** * Let the user input a name for the selected item(s) and trigger * a renaming afterwards. */ void slotRename(); /** * Moves the selected items of the active view to the trash. * This methods adds "shift means del" handling. */ void slotTrashActivated(Qt::MouseButtons, Qt::KeyboardModifiers); /** * Deletes the selected items of the active view. */ void slotDeleteItems(); /** * Switches between showing a preview of the file content and showing the icon. */ 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(); /** Decreases the size of the current set view mode. */ void zoomOut(); /** Switches between an ascending and descending sorting order. */ void toggleSortOrder(); /** Switches between a separate sorting and a mixed sorting of files and folders. */ void toggleSortFoldersFirst(); /** * Updates the state of the 'Sort Ascending/Descending' action. */ void slotSortOrderChanged(Qt::SortOrder order); /** * Updates the state of the 'Sort Folders First' action. */ void slotSortFoldersFirstChanged(bool foldersFirst); /** * Updates the state of the 'Sort by' actions. */ void slotSortingChanged(DolphinView::Sorting sorting); /** * 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 * as specified by \a action. */ void toggleAdditionalInfo(QAction* action); /** * Changes the sorting of the current view. */ void slotSortTriggered(QAction*); /** * Updates the state of the 'Additional Information' actions. */ - void slotAdditionalInfoChanged(); + void slotAdditionalInfoListChanged(const QList& current, + const QList& previous); /** * Switches between sorting by categories or not. */ void toggleSortCategorization(bool); /** * Updates the state of the 'Categorized sorting' menu action. */ - void slotCategorizedSortingChanged(); + void slotCategorizedSortingChanged(bool sortCategorized); /** * Switches between showing and hiding of hidden marked files */ void toggleShowHiddenFiles(bool); /** * 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 * of the currently active view. */ void slotAdjustViewProperties(); /** * Connected to the "properties" action. * Opens the properties dialog for the selected items of the * active view. The properties dialog shows information * like name, size and permissions. */ void slotProperties(); private: /** * Create all the actions. * This is called only once (by the constructor) */ void createActions(); /** * Creates an action group with all the "show additional information" actions in it. * Helper method for createActions(); */ QActionGroup* createAdditionalInformationActionGroup(); /** * Creates an action group with all the "sort by" actions in it. * Helper method for createActions(); */ QActionGroup* createSortByActionGroup(); /** * Returns the "switch to icons mode" action. * Helper method for createActions(); */ 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; }; #endif /* DOLPHINVIEWACTIONHANDLER_H */ 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 * - * * - * 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 -#include -#include -#include -#include -#include - -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& 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 actions) -{ - m_versionControlActions = actions; -} - -QList 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(m_itemView->model()); - if (proxyModel) { - KDirModel* dirModel = static_cast(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 * - * * - * 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 -#include -#include -#include - -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& customActions = QList()); - - /** - * 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 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 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 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 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 @@ -1,216 +1,213 @@ /*************************************************************************** * Copyright (C) 2006-2010 by Peter Penz (peter.penz@gmx.at) * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "renamedialog.h" #include #include #include #include #include #include /** * Helper function for sorting items with qSort() in * DolphinView::renameSelectedItems(). */ bool lessThan(const KFileItem& item1, const KFileItem& item2) { return KStringHandler::naturalCompare(item1.name(), item2.name()) < 0; } RenameDialog::RenameDialog(QWidget *parent, const KFileItemList& items) : KDialog(parent), m_renameOneItem(false), m_newName(), m_lineEdit(0), m_items(items) { const QSize minSize = minimumSize(); setMinimumSize(QSize(320, minSize.height())); const int itemCount = items.count(); Q_ASSERT(itemCount >= 1); m_renameOneItem = (itemCount == 1); setCaption(m_renameOneItem ? i18nc("@title:window", "Rename Item") : i18nc("@title:window", "Rename Items")); setButtons(Ok | Cancel); setDefaultButton(Ok); setButtonGuiItem(Ok, KGuiItem(i18nc("@action:button", "&Rename"), "dialog-ok-apply")); QWidget* page = new QWidget(this); setMainWidget(page); QVBoxLayout* topLayout = new QVBoxLayout(page); QLabel* editLabel = 0; if (m_renameOneItem) { - m_newName = items.first().name(); - editLabel = new QLabel(i18nc("@label:textbox", "Rename the item %1 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 %1 to:", m_newName), + page); } else { m_newName = i18nc("@info:status", "New name #"); editLabel = new QLabel(i18ncp("@label:textbox", "Rename the %1 selected item to:", "Rename the %1 selected items to:", itemCount), page); } m_lineEdit = new KLineEdit(page); connect(m_lineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString))); QString fileName = items[0].url().prettyUrl(); QString extension = KMimeType::extractKnownExtension(fileName.toLower()); if (!extension.isEmpty()) { extension.insert(0, '.'); // The first item seems to have a extension (e. g. '.jpg' or '.txt'). Now // check whether all other URLs have the same extension. If this is the // case, add this extension to the name suggestion. for (int i = 1; i < itemCount; ++i) { fileName = items[i].url().prettyUrl().toLower(); if (!fileName.endsWith(extension)) { // at least one item does not have the same extension extension.truncate(0); break; } } } int selectionLength = m_newName.length(); if (!m_renameOneItem) { --selectionLength; // don't select the # character } const int extensionLength = extension.length(); if (extensionLength > 0) { if (m_renameOneItem) { selectionLength -= extensionLength; } else { m_newName.append(extension); } } m_lineEdit->setText(m_newName); m_lineEdit->setSelection(0, selectionLength); m_lineEdit->setFocus(); topLayout->addWidget(editLabel); topLayout->addWidget(m_lineEdit); if (!m_renameOneItem) { QLabel* infoLabel = new QLabel(i18nc("@info", "(# will be replaced by ascending numbers)"), page); topLayout->addWidget(infoLabel); } } RenameDialog::~RenameDialog() { } void RenameDialog::slotButtonClicked(int button) { if (button == KDialog::Ok) { m_newName = m_lineEdit->text(); if (m_renameOneItem) { Q_ASSERT(m_items.count() == 1); const KUrl oldUrl = m_items.first().url(); KUrl newUrl = oldUrl; newUrl.setFileName(KIO::encodeFileName(m_newName)); KonqOperations::rename(this, oldUrl, newUrl); } else { renameItems(); } } KDialog::slotButtonClicked(button); } void RenameDialog::slotTextChanged(const QString& newName) { bool enable = !newName.isEmpty() && (newName != QLatin1String("..")) && (newName != QLatin1String(".")); if (enable) { if (m_renameOneItem) { enable = enable && (newName != m_newName); } else { // Assure that the new name contains exactly one # (or a connected sequence of #'s) const int minSplitCount = 1; int maxSplitCount = 2; if (newName.startsWith(QLatin1Char('#'))) { --maxSplitCount; } if (newName.endsWith(QLatin1Char('#'))) { --maxSplitCount; } const int splitCount = newName.split(QLatin1Char('#'), QString::SkipEmptyParts).count(); enable = enable && (splitCount >= minSplitCount) && (splitCount <= maxSplitCount); } } enableButtonOk(enable); } void RenameDialog::renameItems() { // Currently the items are sorted by the selection order, resort // them by the filename. This assures that the new sort order is similar to // the current filename sort order. qSort(m_items.begin(), m_items.end(), lessThan); // Iterate through all items and rename them... int index = 1; foreach (const KFileItem& item, m_items) { const QString newName = indexedName(m_newName, index, QLatin1Char('#')); ++index; const KUrl oldUrl = item.url(); if (oldUrl.fileName() != newName) { KUrl newUrl = oldUrl; newUrl.setFileName(KIO::encodeFileName(newName)); KonqOperations::rename(this, oldUrl, newUrl); } } } QString RenameDialog::indexedName(const QString& name, int index, const QChar& indexPlaceHolder) { QString newName = name; QString indexString = QString::number(index); // Insert leading zeros if necessary const int minIndexLength = name.count(indexPlaceHolder); while (indexString.length() < minIndexLength) { indexString.prepend(QLatin1Char('0')); } // Replace the index placeholders by the indexString const int placeHolderStart = newName.indexOf(indexPlaceHolder); newName.replace(placeHolderStart, minIndexLength, indexString); return newName; } #include "renamedialog.moc" 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 * - * * - * 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 -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -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(m_view->model()); - KDirModel* dirModel = static_cast(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(m_view->model()); - KDirModel* dirModel = static_cast(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 * - * * - * 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 - -#include - -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 * - * * - * 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 -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -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 * - * * - * 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 - -#include -#include - -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 @@ -1,307 +1,303 @@ /******************************************************************************* * Copyright (C) 2008 by Konstantin Heil * * * * 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 "tooltipmanager.h" #include "filemetadatatooltip.h" #include #include #include #include #include +#include #include #include #include -#include -#include - -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), m_toolTipRequested(false), m_metaDataRequested(false), m_appliedWaitCursor(false), m_item(), m_itemRect() { - m_dolphinModel = static_cast(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(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); m_showToolTipTimer->setSingleShot(true); m_showToolTipTimer->setInterval(500); connect(m_showToolTipTimer, SIGNAL(timeout()), this, SLOT(showToolTip())); m_contentRetrievalTimer = new QTimer(this); m_contentRetrievalTimer->setSingleShot(true); m_contentRetrievalTimer->setInterval(200); connect(m_contentRetrievalTimer, SIGNAL(timeout()), this, SLOT(startContentRetrieval())); Q_ASSERT(m_contentRetrievalTimer->interval() < m_showToolTipTimer->interval()); // 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() { } void ToolTipManager::hideToolTip() { if (m_appliedWaitCursor) { QApplication::restoreOverrideCursor(); m_appliedWaitCursor = false; } m_toolTipRequested = false; m_metaDataRequested = false; m_showToolTipTimer->stop(); m_contentRetrievalTimer->stop(); delete m_fileMetaDataToolTip; m_fileMetaDataToolTip = 0; } 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: hideToolTip(); break; default: break; } } 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 // meta data retrieval, when passing rapidly over a lot of items. Q_ASSERT(!m_fileMetaDataToolTip); m_fileMetaDataToolTip = new FileMetaDataToolTip(m_view); connect(m_fileMetaDataToolTip, SIGNAL(metaDataRequestFinished(KFileItemList)), this, SLOT(slotMetaDataRequestFinished())); m_contentRetrievalTimer->start(); m_showToolTipTimer->start(); m_toolTipRequested = true; Q_ASSERT(!m_metaDataRequested); } } void ToolTipManager::startContentRetrieval() { if (!m_toolTipRequested) { return; } m_fileMetaDataToolTip->setName(m_item.text()); // Request the retrieval of meta-data. The slot // slotMetaDataRequestFinished() is invoked after the // meta-data have been received. m_metaDataRequested = true; m_fileMetaDataToolTip->setItems(KFileItemList() << m_item); m_fileMetaDataToolTip->adjustSize(); // Request a preview of the item m_fileMetaDataToolTip->setPreview(QPixmap()); KIO::PreviewJob* job = KIO::filePreview(KFileItemList() << m_item, QSize(256, 256)); connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)), this, SLOT(setPreviewPix(const KFileItem&, const QPixmap&))); connect(job, SIGNAL(failed(const KFileItem&)), this, SLOT(previewFailed())); } void ToolTipManager::setPreviewPix(const KFileItem& item, const QPixmap& pixmap) { if (!m_toolTipRequested || (m_item.url() != item.url())) { // No tooltip is requested anymore or an old preview has been received return; } if (pixmap.isNull()) { previewFailed(); } else { m_fileMetaDataToolTip->setPreview(pixmap); if (!m_showToolTipTimer->isActive()) { showToolTip(); } } } void ToolTipManager::previewFailed() { if (!m_toolTipRequested) { return; } const QPixmap pixmap = KIcon(m_item.iconName()).pixmap(128, 128); m_fileMetaDataToolTip->setPreview(pixmap); if (!m_showToolTipTimer->isActive()) { showToolTip(); } } void ToolTipManager::slotMetaDataRequestFinished() { if (!m_toolTipRequested) { return; } m_metaDataRequested = false; if (!m_showToolTipTimer->isActive()) { showToolTip(); } } void ToolTipManager::showToolTip() { Q_ASSERT(m_toolTipRequested); if (m_appliedWaitCursor) { QApplication::restoreOverrideCursor(); m_appliedWaitCursor = false; } if (QApplication::mouseButtons() & Qt::LeftButton) { return; } if (m_fileMetaDataToolTip->preview().isNull() || m_metaDataRequested) { Q_ASSERT(!m_appliedWaitCursor); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_appliedWaitCursor = true; return; } const QRect screen = QApplication::desktop()->screenGeometry(QCursor::pos()); // Restrict tooltip size to current screen size when needed. // Because layout controlling widget doesn't respect widget's maximumSize property // (correct me if I'm wrong), we need to let layout do its work, then manually change // geometry if resulting widget doesn't fit the screen. // Step #1 - make sizeHint return calculated tooltip size m_fileMetaDataToolTip->layout()->setSizeConstraint(QLayout::SetFixedSize); m_fileMetaDataToolTip->adjustSize(); QSize size = m_fileMetaDataToolTip->sizeHint(); // Step #2 - correct tooltip size when needed if (size.width() > screen.width()) { size.setWidth(screen.width()); } if (size.height() > screen.height()) { size.setHeight(screen.height()); } // m_itemRect defines the area of the item, where the tooltip should be // shown. Per default the tooltip is shown centered at the bottom. // It must be assured that: // - the content is fully visible // - the content is not drawn inside m_itemRect const int margin = 3; const bool hasRoomToLeft = (m_itemRect.left() - size.width() - margin >= screen.left()); const bool hasRoomToRight = (m_itemRect.right() + size.width() + margin <= screen.right()); const bool hasRoomAbove = (m_itemRect.top() - size.height() - margin >= screen.top()); const bool hasRoomBelow = (m_itemRect.bottom() + size.height() + margin <= screen.bottom()); if (!hasRoomAbove && !hasRoomBelow && !hasRoomToLeft && !hasRoomToRight) { return; } int x, y; if (hasRoomBelow || hasRoomAbove) { x = qMax(screen.left(), m_itemRect.center().x() - size.width() / 2); if (x + size.width() >= screen.right()) { x = screen.right() - size.width() + 1; } if (hasRoomBelow) { y = m_itemRect.bottom() + margin; } else { y = m_itemRect.top() - size.height() - margin; } } else { Q_ASSERT(hasRoomToLeft || hasRoomToRight); if (hasRoomToRight) { x = m_itemRect.right() + margin; } else { x = m_itemRect.left() - size.width() - margin; } // Put the tooltip at the bottom of the screen. The x-coordinate has already // been adjusted, so that no overlapping with m_itemRect occurs. y = screen.bottom() - size.height() + 1; } // Step #3 - Alter tooltip geometry m_fileMetaDataToolTip->setFixedSize(size); m_fileMetaDataToolTip->layout()->setSizeConstraint(QLayout::SetNoConstraint); m_fileMetaDataToolTip->move(QPoint(x, y)); m_fileMetaDataToolTip->show(); m_toolTipRequested = false; } #include "tooltipmanager.moc" 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 @@ -1,92 +1,91 @@ /******************************************************************************* * Copyright (C) 2008 by Konstantin Heil * * * * 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 TOOLTIPMANAGER_H #define TOOLTIPMANAGER_H #include #include #include class DolphinModel; class DolphinSortFilterProxyModel; class FileMetaDataToolTip; class QAbstractItemView; class QModelIndex; class QTimer; /** * @brief Manages the tooltips for an item view. * * When hovering an item, a tooltip is shown after * a short timeout. The tooltip is hidden again when the * viewport is hovered or the item view has been left. */ class ToolTipManager : public QObject { Q_OBJECT public: - explicit ToolTipManager(QAbstractItemView* parent, - DolphinSortFilterProxyModel* model); + explicit ToolTipManager(QWidget* parent); virtual ~ToolTipManager(); public slots: /** * Hides the currently shown tooltip. Invoking this method is * only needed when the tooltip should be hidden although * an item is hovered. */ void hideToolTip(); protected: virtual bool eventFilter(QObject* watched, QEvent* event); private slots: void requestToolTip(const QModelIndex& index); void startContentRetrieval(); void setPreviewPix(const KFileItem& item, const QPixmap& pix); void previewFailed(); void slotMetaDataRequestFinished(); void showToolTip(); private: - QAbstractItemView* m_view; + QWidget* m_view; DolphinModel* m_dolphinModel; DolphinSortFilterProxyModel* m_proxyModel; /// Timeout from requesting a tooltip until the tooltip /// should be shown QTimer* m_showToolTipTimer; /// Timeout from requesting a tooltip until the retrieving of /// the tooltip content like preview and meta data gets started. QTimer* m_contentRetrievalTimer; FileMetaDataToolTip* m_fileMetaDataToolTip; bool m_toolTipRequested; bool m_metaDataRequested; bool m_appliedWaitCursor; KFileItem m_item; QRect m_itemRect; }; #endif 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 @@ -1,337 +1,338 @@ /*************************************************************************** * Copyright (C) 2009 by Peter Penz * * * * 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 "versioncontrolobserver.h" #include "dolphin_versioncontrolsettings.h" #include #include #include #include #include #include "pendingthreadsmaintainer.h" #include "updateitemstatesthread.h" #include #include #include #include -#include - -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(view->model()); + /*QAbstractProxyModel* proxyModel = qobject_cast(view->model()); m_dolphinModel = proxyModel ? qobject_cast(proxyModel->sourceModel()) : qobject_cast(view->model()); if (m_dolphinModel) { m_dirLister = m_dolphinModel->dirLister(); connect(m_dirLister, SIGNAL(completed()), this, SLOT(delayedDirectoryVerification())); // The verification timer specifies the timeout until the shown directory // is checked whether it is versioned. Per default it is assumed that users // don't iterate through versioned directories and a high timeout is used // The timeout will be decreased as soon as a versioned directory has been // found (see verifyDirectory()). m_dirVerificationTimer = new QTimer(this); m_dirVerificationTimer->setSingleShot(true); m_dirVerificationTimer->setInterval(500); connect(m_dirVerificationTimer, SIGNAL(timeout()), this, SLOT(verifyDirectory())); - } + }*/ } VersionControlObserver::~VersionControlObserver() { if (m_updateItemStatesThread) { if (m_updateItemStatesThread->isFinished()) { delete m_updateItemStatesThread; m_updateItemStatesThread = 0; } else { // The version controller gets deleted, while a thread still // is working to get the version information. To avoid a blocking // user interface, the thread will be forwarded to the // PendingThreadsMaintainer, which will delete the thread later. disconnect(m_updateItemStatesThread, SIGNAL(finished()), this, SLOT(slotThreadFinished())); PendingThreadsMaintainer::instance().append(m_updateItemStatesThread); m_updateItemStatesThread = 0; } } if (m_plugin) { m_plugin->disconnect(); m_plugin = 0; } } QList VersionControlObserver::contextMenuActions(const KFileItemList& items) const { QList actions; if (isVersioned() && m_updateItemStatesThread->lockPlugin()) { actions = m_plugin->contextMenuActions(items); m_updateItemStatesThread->unlockPlugin(); } return actions; } QList VersionControlObserver::contextMenuActions(const QString& directory) const { QList actions; if (isVersioned() && m_updateItemStatesThread->lockPlugin()) { actions = m_plugin->contextMenuActions(directory); m_updateItemStatesThread->unlockPlugin(); } return actions; } void VersionControlObserver::delayedDirectoryVerification() { m_silentUpdate = false; m_dirVerificationTimer->start(); } void VersionControlObserver::silentDirectoryVerification() { m_silentUpdate = true; m_dirVerificationTimer->start(); } void VersionControlObserver::verifyDirectory() { - const KUrl versionControlUrl = m_dirLister->url(); + const KUrl versionControlUrl; // = m_dirLister->url(); if (!versionControlUrl.isLocalFile()) { return; } if (m_plugin) { m_plugin->disconnect(); } m_plugin = searchPlugin(versionControlUrl); - if (m_plugin) { + /*if (m_plugin) { connect(m_plugin, SIGNAL(versionStatesChanged()), this, SLOT(silentDirectoryVerification())); connect(m_plugin, SIGNAL(infoMessage(QString)), this, SIGNAL(infoMessage(QString))); connect(m_plugin, SIGNAL(errorMessage(QString)), this, SIGNAL(errorMessage(QString))); connect(m_plugin, SIGNAL(operationCompletedMessage(QString)), this, SIGNAL(operationCompletedMessage(QString))); if (!m_versionedDirectory) { m_versionedDirectory = true; // The directory is versioned. Assume that the user will further browse through // versioned directories and decrease the verification timer. m_dirVerificationTimer->setInterval(100); connect(m_dirLister, SIGNAL(refreshItems(const QList>&)), this, SLOT(delayedDirectoryVerification())); connect(m_dirLister, SIGNAL(newItems(const KFileItemList&)), this, SLOT(delayedDirectoryVerification())); } updateItemStates(); } else if (m_versionedDirectory) { m_versionedDirectory = false; // The directory is not versioned. Reset the verification timer to a higher // value, so that browsing through non-versioned directories is not slown down // by an immediate verification. m_dirVerificationTimer->setInterval(500); disconnect(m_dirLister, SIGNAL(refreshItems(const QList>&)), this, SLOT(delayedDirectoryVerification())); disconnect(m_dirLister, SIGNAL(newItems(const KFileItemList&)), this, SLOT(delayedDirectoryVerification())); - } + }*/ } void VersionControlObserver::slotThreadFinished() { if (!m_plugin) { return; } if (!m_updateItemStatesThread->retrievedItems()) { // ignore m_silentUpdate for an error message emit errorMessage(i18nc("@info:status", "Update of version information failed.")); return; } // QAbstractItemModel::setData() triggers a bottleneck in combination with QListView // (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 itemStates = m_updateItemStatesThread->itemStates(); foreach (const ItemState& itemState, itemStates) { m_dolphinModel->setData(itemState.index, QVariant(static_cast(itemState.version)), Qt::DecorationRole); } m_dolphinModel->blockSignals(signalsBlocked); m_view->viewport()->repaint(); if (!m_silentUpdate) { // Using an empty message results in clearing the previously shown information message and showing // the default status bar information. This is useful as the user already gets feedback that the // operation has been completed because of the icon emblems. emit operationCompletedMessage(QString()); } if (m_pendingItemStatesUpdate) { m_pendingItemStatesUpdate = false; updateItemStates(); - } + }*/ } void VersionControlObserver::updateItemStates() { Q_ASSERT(m_plugin); if (!m_updateItemStatesThread) { m_updateItemStatesThread = new UpdateItemStatesThread(); connect(m_updateItemStatesThread, SIGNAL(finished()), this, SLOT(slotThreadFinished())); } if (m_updateItemStatesThread->isRunning()) { // An update is currently ongoing. Wait until the thread has finished // the update (see slotThreadFinished()). m_pendingItemStatesUpdate = true; return; } QList itemStates; addDirectory(QModelIndex(), itemStates); if (!itemStates.isEmpty()) { if (!m_silentUpdate) { emit infoMessage(i18nc("@info:status", "Updating version information...")); } m_updateItemStatesThread->setData(m_plugin, itemStates); m_updateItemStatesThread->start(); // slotThreadFinished() is called when finished } } void VersionControlObserver::addDirectory(const QModelIndex& parentIndex, QList& 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); ItemState itemState; itemState.index = index; itemState.item = m_dolphinModel->itemForIndex(index); itemState.version = KVersionControlPlugin::UnversionedVersion; itemStates.append(itemState); - } + }*/ } KVersionControlPlugin* VersionControlObserver::searchPlugin(const KUrl& directory) const { static bool pluginsAvailable = true; static QList plugins; if (!pluginsAvailable) { // A searching for plugins has already been done, but no // plugins are installed return 0; } if (plugins.isEmpty()) { // No searching for plugins has been done yet. Query the KServiceTypeTrader for // all fileview version control plugins and remember them in 'plugins'. const QStringList enabledPlugins = VersionControlSettings::enabledPlugins(); const KService::List pluginServices = KServiceTypeTrader::self()->query("FileViewVersionControlPlugin"); for (KService::List::ConstIterator it = pluginServices.constBegin(); it != pluginServices.constEnd(); ++it) { if (enabledPlugins.contains((*it)->name())) { KVersionControlPlugin* plugin = (*it)->createInstance(); if (plugin) { plugins.append(plugin); } } } if (plugins.isEmpty()) { pluginsAvailable = false; return 0; } } // 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; fileUrl.addPath(plugin->fileName()); const KFileItem item = m_dirLister->findByUrl(fileUrl); if (!item.isNull()) { return plugin; } // Version control systems like Git provide the version information // file only in the root directory. Check whether the version information file can // be found in one of the parent directories. For performance reasons this // step is only done, if the previous directory was marked as versioned by // m_versionedDirectory. Drawback: Until e. g. Git is recognized, the root directory // must be shown at least once. if (m_versionedDirectory) { KUrl upUrl = dirUrl.upUrl(); while (upUrl != dirUrl) { const QString filePath = dirUrl.pathOrUrl(KUrl::AddTrailingSlash) + plugin->fileName(); QFileInfo file(filePath); if (file.exists()) { return plugin; } dirUrl = upUrl; 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 @@ -1,149 +1,149 @@ /*************************************************************************** * Copyright (C) 2009 by Peter Penz * * * * 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 VERSIONCONTROLOBSERVER_H #define VERSIONCONTROLOBSERVER_H #include #include #include #include #include #include #include #include class DolphinModel; class KDirLister; class KFileItemList; class QAbstractItemView; class QAction; class QTimer; class UpdateItemStatesThread; /** * @brief Observes all version control plugins. * * The item view gets updated automatically if the currently shown * directory is under version control. * * @see VersionControlPlugin */ class LIBDOLPHINPRIVATE_EXPORT VersionControlObserver : public QObject { Q_OBJECT public: - VersionControlObserver(QAbstractItemView* view); + VersionControlObserver(QWidget* parent); virtual ~VersionControlObserver(); QList contextMenuActions(const KFileItemList& items) const; QList contextMenuActions(const QString& directory) const; signals: /** * Is emitted if an information message with the content \a msg * should be shown. */ void infoMessage(const QString& msg); /** * Is emitted if an error message with the content \a msg * should be shown. */ void errorMessage(const QString& msg); /** * Is emitted if an "operation completed" message with the content \a msg * should be shown. */ void operationCompletedMessage(const QString& msg); private slots: /** * Invokes verifyDirectory() with a small delay. If delayedDirectoryVerification() * is invoked before the delay has been exceeded, the delay will be reset. This * assures that a lot of short requests for directory verification only result * in one (expensive) call. */ void delayedDirectoryVerification(); /** * Invokes verifyDirectory() with a small delay. In opposite to * delayedDirectoryVerification() it and assures that the verification of * the directory is done silently without information messages. */ void silentDirectoryVerification(); void verifyDirectory(); /** * Is invoked if the thread m_updateItemStatesThread has been finished * and applys the item states. */ void slotThreadFinished(); private: struct ItemState { QPersistentModelIndex index; KFileItem item; KVersionControlPlugin::VersionState version; }; void updateItemStates(); /** * Adds recursively all items from the directory \p parentIndex into * the list \p itemStates. */ void addDirectory(const QModelIndex& parentIndex, QList& itemStates); /** * Returns a matching plugin for the given directory. * 0 is returned, if no matching plugin has been found. */ KVersionControlPlugin* searchPlugin(const KUrl& directory) const; /** * Returns true, if the directory contains a version control information. */ bool isVersioned() const; private: bool m_pendingItemStatesUpdate; bool m_versionedDirectory; 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; KVersionControlPlugin* m_plugin; UpdateItemStatesThread* m_updateItemStatesThread; friend class UpdateItemStatesThread; }; #endif // REVISIONCONTROLOBSERVER_H 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 * - * * - * 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 -#include -#include -#include -#include - -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(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(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 actions; - if (items.isEmpty()) { - const KDirModel* dirModel = static_cast(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(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 * - * * - * 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 - -#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 @@ -1,451 +1,383 @@ /*************************************************************************** * Copyright (C) 2006-2010 by Peter Penz * * Copyright (C) 2006 by Aaron J. Seigo * * * * 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 "viewproperties.h" #include "additionalinfoaccessor.h" #include "dolphin_directoryviewpropertysettings.h" #include "dolphin_generalsettings.h" #include #include #include #include #include #include #include #include "settings/dolphinsettings.h" 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"; } ViewProperties::ViewProperties(const KUrl& url) : m_changedProps(false), m_autoSave(true), m_node(0) { GeneralSettings* settings = DolphinSettings::instance().generalSettings(); const bool useGlobalViewProps = settings->globalViewProps(); bool useDetailsViewWithPath = false; // We try and save it to the file .directory in the directory being viewed. // If the directory is not writable by the user or the directory is not local, // we store the properties information in a local file. if (useGlobalViewProps) { m_filePath = destinationDir("global"); } else if (url.protocol().contains("search")) { m_filePath = destinationDir("search"); useDetailsViewWithPath = true; } else if (url.protocol() == QLatin1String("trash")) { m_filePath = destinationDir("trash"); useDetailsViewWithPath = true; } else if (url.isLocalFile()) { m_filePath = url.toLocalFile(); const QFileInfo info(m_filePath); if (!info.isWritable() || !isPartOfHome(m_filePath)) { m_filePath = destinationDir("local") + m_filePath; } } else { m_filePath = destinationDir("remote") + m_filePath; } const QString file = m_filePath + QDir::separator() + QLatin1String(".directory"); m_node = new ViewPropertySettings(KSharedConfig::openConfig(file)); // If the .directory file does not exist or the timestamp is too old, // use default values instead. const bool useDefaultProps = (!useGlobalViewProps || useDetailsViewWithPath) && (!QFileInfo(file).exists() || (m_node->timestamp() < settings->viewPropsTimestamp())); if (useDefaultProps) { if (useDetailsViewWithPath) { setViewMode(DolphinView::DetailsView); - setAdditionalInfo(KFileItemDelegate::InformationList() << KFileItemDelegate::LocalPathOrUrl); + setAdditionalInfoList(QList() << DolphinView::PathInfo); } else { // The global view-properties act as default for directories without // any view-property configuration settings->setGlobalViewProps(true); ViewProperties defaultProps(url); setDirProperties(defaultProps); settings->setGlobalViewProps(false); m_changedProps = false; } } } ViewProperties::~ViewProperties() { if (m_changedProps && m_autoSave) { save(); } delete m_node; m_node = 0; } void ViewProperties::setViewMode(DolphinView::Mode mode) { if (m_node->viewMode() != mode) { m_node->setViewMode(mode); update(); } } DolphinView::Mode ViewProperties::viewMode() const { return static_cast(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(); } } void ViewProperties::setCategorizedSorting(bool categorized) { if (m_node->categorizedSorting() != categorized) { m_node->setCategorizedSorting(categorized); update(); } } 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) { if (m_node->sorting() != sorting) { m_node->setSorting(sorting); update(); } } DolphinView::Sorting ViewProperties::sorting() const { return static_cast(m_node->sorting()); } void ViewProperties::setSortOrder(Qt::SortOrder sortOrder) { if (m_node->sortOrder() != sortOrder) { m_node->setSortOrder(sortOrder); update(); } } Qt::SortOrder ViewProperties::sortOrder() const { return static_cast(m_node->sortOrder()); } void ViewProperties::setSortFoldersFirst(bool foldersFirst) { if (m_node->sortFoldersFirst() != foldersFirst) { m_node->setSortFoldersFirst(foldersFirst); update(); } } bool ViewProperties::sortFoldersFirst() const { return m_node->sortFoldersFirst(); } -void ViewProperties::setAdditionalInfo(const KFileItemDelegate::InformationList& list) +void ViewProperties::setAdditionalInfoList(const QList& 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) { if (newInfoStringList.at(i).startsWith(prefix)) { newInfoStringList.removeAt(i); } } // 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)); } // Only update the information if it has been changed bool changed = oldInfoStringList.count() != newInfoStringList.count(); if (!changed) { foreach (const QString& oldInfoString, oldInfoStringList) { if (!newInfoStringList.contains(oldInfoString)) { changed = true; break; } } } if (changed) { - if (m_node->version() < 2) { - m_node->setVersion(2); - } - const bool markCustomizedDetails = (m_node->viewMode() == DolphinView::DetailsView) && !newInfoStringList.contains(CustomizedDetailsString); if (markCustomizedDetails) { // The additional information of the details-view has been modified. Set a marker, // so that it is allowed to also show no additional information // (see fallback in ViewProperties::additionalInfoV2, if no additional information is // available). newInfoStringList.append(CustomizedDetailsString); } - m_node->setAdditionalInfoV2(newInfoStringList); + m_node->setAdditionalInfo(newInfoStringList); update(); } } -KFileItemDelegate::InformationList ViewProperties::additionalInfo() const +QList 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, 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. + + QList usedInfo; + + // infoHash allows to get the mapped DolphinView::AdditionalInfo value + // for a stored string-value in a fast way + static QHash infoHash; + if (infoHash.isEmpty()) { + AdditionalInfoAccessor& infoAccessor = AdditionalInfoAccessor::instance(); + const QList 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"; + } + } + } - switch (m_node->version()) { - case 1: usedInfo = additionalInfoV1(); break; - case 2: usedInfo = additionalInfoV2(); break; - default: kWarning() << "Unknown version of the view properties"; + // 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) { m_autoSave = autoSave; } bool ViewProperties::isAutoSaveEnabled() const { return m_autoSave; } 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() { KStandardDirs::makeDir(m_filePath); m_node->writeConfig(); m_changedProps = false; } KUrl ViewProperties::mirroredDirectory() { QString basePath = KGlobal::mainComponent().componentName(); basePath.append("/view_properties/"); return KUrl(KStandardDirs::locateLocal("data", basePath)); } QString ViewProperties::destinationDir(const QString& subDir) const { QString basePath = KGlobal::mainComponent().componentName(); basePath.append("/view_properties/").append(subDir); 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 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"; } return prefix; } bool ViewProperties::isPartOfHome(const QString& filePath) { // For performance reasons cache the path in a static QString // (see QDir::homePath() for more details) static QString homePath; if (homePath.isEmpty()) { homePath = QDir::homePath(); Q_ASSERT(!homePath.isEmpty()); } return filePath.startsWith(homePath); } 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 @@ -1,166 +1,154 @@ /*************************************************************************** * Copyright (C) 2006-2010 by Peter Penz * * Copyright (C) 2006 by Aaron J. Seigo * * * * 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 VIEWPROPERTIES_H #define VIEWPROPERTIES_H #include #include #include class ViewPropertySettings; /** * @brief Maintains the view properties like 'view mode' or * 'show hidden files' for a directory. * * The view properties are automatically stored as part of the file * .directory inside the corresponding path. To read out the view properties * just construct an instance by passing the path of the directory: * * \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 * inside the destructor. * * If no .directory file is available or the global view mode is turned on * (see GeneralSettings::globalViewMode()), the values from the global .directory file * are used for initialization. */ class LIBDOLPHINPRIVATE_EXPORT ViewProperties { public: explicit ViewProperties(const KUrl& url); virtual ~ViewProperties(); 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; void setSorting(DolphinView::Sorting sorting); DolphinView::Sorting sorting() const; void setSortOrder(Qt::SortOrder sortOrder); Qt::SortOrder sortOrder() const; void setSortFoldersFirst(bool foldersFirst); bool sortFoldersFirst() const; /** * Sets 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). */ - void setAdditionalInfo(const KFileItemDelegate::InformationList& info); + void setAdditionalInfoList(const QList& 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 additionalInfoList() const; /** * Sets the directory properties view mode, show preview, * show hidden files, sorting and sort order like * set in \a props. */ void setDirProperties(const ViewProperties& props); /** * If \a autoSave is true, the properties are automatically * saved when the destructor is called. Per default autosaving * is enabled. */ void setAutoSaveEnabled(bool autoSave); bool isAutoSaveEnabled() const; void update(); /** * Saves the view properties for the directory specified * in the constructor. The method is automatically * invoked in the destructor, if * ViewProperties::isAutoSaveEnabled() returns true and * at least one property has been changed. */ void save(); /** * Returns the URL of the directory, where the mirrored view properties * are stored into. Mirrored view properties are used if: * - there is no write access for storing the view properties into * the original directory * - for non local directories */ static KUrl mirroredDirectory(); private: /** * Returns the destination directory path where the view * properties are stored. \a subDir specifies the used sub * directory. */ 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. */ QString viewModePrefix() const; /** * Returns true, if \a filePath is part of the home-path (see QDir::homePath()). */ static bool isPartOfHome(const QString& filePath); Q_DISABLE_COPY(ViewProperties) private: bool m_changedProps; bool m_autoSave; QString m_filePath; ViewPropertySettings* m_node; }; #endif