Added new versions, mangled some symbols

ubuntu/cosmic
Alf Gaida 7 years ago
parent f95b471116
commit 0829311d25

@ -1,6 +1,6 @@
Upstream Authors:
LXQt team: http://lxqt.org
LXQt team: https://lxqt.org
Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
Copyright:
Copyright (c) 2013-2017 LXQt team
Copyright (c) 2013-2018 LXQt team

@ -1,7 +1,86 @@
libfm-qt-0.12.0 / 2017-10-21
libfm-qt-0.13.0 / 2018-05-21
============================
* Bumped minor version to 13
* Fixed shortcut detection
* Delete CachedFolderModel immediately on unreffing it
* Fix crashes in new templates code
* Remove the C++ wrapper for the old FmPath struct in libfm.
* Avoid using FmPath from libfm in FmSearch.
* Removed redundant code
* Modify Fm::BasicFileLauncher APIs to use Fm::FileInfoPtr consistently.
* Add typedef FileInfoPtr for std::shared_ptr<const FileInfo> since it's so frequently used.
* Correctly handle the mounting of mountable paths and correctly parse the target URIs of shortcuts.
* Add new APIs Fm::MountOperation::mountEnclosingVolume() and Fm::MountOperation::mountMountable() and deprecate Fm::MountOperation::mount().
* Also cover local shortcuts
* Added "reject" to exec dialog
* Delete some libfm compatibility wrappers.
* Delete the unused old FmTemplate wrapper.
* Create the template items in the "Create New" menu with the new C++ file template APIs.
* Port template support to C++ (Fm::Templates class).
* Add convinient functions in Fm::FileOperation to set dest paths for file transfer jobs.
* Made job async again
* Fixed launching of desktop files
* Cleanup
* CMake: Prevent in-source builds
* Fix Fm::FileInfo::isDir() and isExecutableType() to make its behavior consistent with libfm.
* Port file lancher to C++ and Implement Fm::BasicFileLauncher base class. * Migrate Fm::FileLauncher to the new C++ implementation.
* Port archiver integration to C++ (#182)
* Fully port all file operations to C++ 11 (#181)
* Add kitty to terminals.list
* Italic font for hidden items
* Select multiple files
* No visible cursor in File Properties labels
* Just corrected a misspelling
* Elided labels for file operation dialog
* fix namespace, fixes lxqt/libfm-qt/issues/174
* Misc link fixes
* Fixes some pathes after repo move
* Fix a cause of crash in `AppChooserComboBox`
* build: Bump version
* Drop Fm::IconTheme
* iconinfo: Properly handle multiple names
* Some code cleanup
* Fixed custom action execution mode
* Don't drop on files
* Prevent possible c++11 range-loop container detach
* See `.bak` and `.old` as backup extensions; also follow GLib
* Ensure that rename editor has opaque background
* Use special/custom folder icons for bookmarks
* Added two missing cases of `mapFromSource()`
* Support hiding items in Places side-pane
* Fixed sorting by type/owner
* Fix lambda connections in filedialog (#159)
* Drop Q_FOREACH
* Fix memory leak in thumbnail loading (#150)
* Be more tolerant
* Fix the "Folders" key in custom actions
* Add Group column and don't use owner's full name
* Fix comparison of integers of different signs
* Guarantee 64-bit time attributes (#148)
* Added a proxy setting for backup as hidden (#145)
* Fixed the logic of queued deletion
* Track folders containing cut files only with QString
* Copy selected pathbar text to selection clipboard
* Smooth scrolling for icon and thumbnail views
* cmake: Handle CMP0071
* Prepare libfm-qt for bulk rename
* No change queue of files in the deletion queue
* FileInfo: Fix potential SEGFAULT
* Fix two devices for one mount
* Always wait for the folder to load before selecting
* Really cancel multiple renaming on cancelling
* Prevent a potential crash in xdndworkaround
* Fix wrong gray-out of cut files
* Merge side-pane with its surroundings
* Prompt dialog specifically for desktop files
* Remove unnecessary noise
0.12.0 / 2017-10-21
===================
* Release 0.12.0: Update changelog
* Add data transferred to file operation dialog.
* Bump versions
* Disable context-menu actions that cannot be used

@ -4,27 +4,27 @@ project(libfm-qt)
set(LIBFM_QT_LIBRARY_NAME "fm-qt" CACHE STRING "fm-qt")
set(LIBFM_QT_VERSION_MAJOR 0)
set(LIBFM_QT_VERSION_MINOR 12)
set(LIBFM_QT_VERSION_MINOR 13)
set(LIBFM_QT_VERSION_PATCH 0)
set(LIBFM_QT_VERSION ${LIBFM_QT_VERSION_MAJOR}.${LIBFM_QT_VERSION_MINOR}.${LIBFM_QT_VERSION_PATCH})
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
# We use the libtool versioning scheme for the internal so name, "current:revision:age"
# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
# https://www.sourceware.org/autobook/autobook/autobook_91.html
# http://pusling.com/blog/?p=352
# Actually, libtool uses different ways on different operating systems. So there is no
# universal way to translate a libtool version-info to a cmake version.
# We use "(current-age).age.revision" as the cmake version.
# current: 4, revision: 0, age: 1 => version: 3.1.0
set(LIBFM_QT_LIB_VERSION "3.1.0")
set(LIBFM_QT_LIB_SOVERSION "3")
# current: 5, revision: 0, age: 0 => version: 5.0.0
set(LIBFM_QT_LIB_VERSION "5.0.0")
set(LIBFM_QT_LIB_SOVERSION "5")
set(REQUIRED_QT_VERSION "5.2")
set(REQUIRED_QT_VERSION "5.7.1")
set(REQUIRED_LIBFM_VERSION "1.2.0")
set(REQUIRED_LIBMENUCACHE_VERSION "0.4.0")
set(REQUIRED_LXQT_BUILD_TOOLS_VERSION "0.4.0")
set(REQUIRED_LXQT_BUILD_TOOLS_VERSION "0.5.0")
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
@ -46,11 +46,13 @@ option(UPDATE_TRANSLATIONS "Update source translation translations/*.ts files" O
include(GNUInstallDirs)
include(GenerateExportHeader)
include(CMakePackageConfigHelpers)
include(LXQtPreventInSourceBuilds)
include(LXQtTranslateTs)
include(LXQtTranslateDesktop)
include(LXQtCompilerSettings NO_POLICY_SCOPE)
set(CMAKE_AUTOMOC TRUE)
set(CMAKE_AUTOUIC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
write_basic_package_version_file(
@ -70,8 +72,7 @@ add_subdirectory(data)
# add Doxygen support to generate API docs
# References:
# http://majewsky.wordpress.com/2010/08/14/tip-of-the-day-cmake-and-doxygen/
# http://www.bluequartz.net/projects/EIM_Segmentation/SoftwareDocumentation/html/usewithcmakeproject.html
# https://majewsky.wordpress.com/2010/08/14/tip-of-the-day-cmake-and-doxygen/
option(BUILD_DOCUMENTATION "Use Doxygen to create the HTML based API documentation" OFF)
if(BUILD_DOCUMENTATION)
find_package(Doxygen REQUIRED)

@ -20,7 +20,7 @@
# that follow. The default is UTF-8 which is also the encoding used for all
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
# iconv built into libc) for the transcoding. See
# http://www.gnu.org/software/libiconv for the list of possible encodings.
# https://www.gnu.org/software/libiconv for the list of possible encodings.
DOXYFILE_ENCODING = UTF-8
@ -247,7 +247,7 @@ EXTENSION_MAPPING =
# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
# comments according to the Markdown format, which allows for more readable
# documentation. See http://daringfireball.net/projects/markdown/ for details.
# documentation. See https://daringfireball.net/projects/markdown/ for details.
# The output of markdown processing is further processed by doxygen, so you
# can mix doxygen, HTML, and XML commands with Markdown formatting.
# Disable only in case of backward compatibilities issues.
@ -587,7 +587,7 @@ LAYOUT_FILE =
# containing the references data. This must be a list of .bib files. The
# .bib extension is automatically appended if omitted. Using this command
# requires the bibtex tool to be installed. See also
# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
# https://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
# feature you need bibtex and perl available in the search path. Do not use
# file names with spaces, bibtex cannot handle them.
@ -659,7 +659,7 @@ INPUT = "@PROJECT_SOURCE_DIR@/src"
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
# also the default input encoding. Doxygen uses libiconv (or the iconv built
# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
# into libc) for the transcoding. See https://www.gnu.org/software/libiconv for
# the list of possible encodings.
INPUT_ENCODING = UTF-8
@ -931,7 +931,7 @@ HTML_EXTRA_FILES =
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
# Doxygen will adjust the colors in the style sheet and background images
# according to this color. Hue is specified as an angle on a colorwheel,
# see http://en.wikipedia.org/wiki/Hue for more information.
# see https://en.wikipedia.org/wiki/Hue for more information.
# For instance the value 0 represents red, 60 is yellow, 120 is green,
# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
# The allowed range is 0 to 359.
@ -984,7 +984,7 @@ HTML_INDEX_NUM_ENTRIES = 100
# directory and running "make install" will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
# it at startup.
# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
# See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html
# for more information.
GENERATE_DOCSET = NO

@ -2,26 +2,44 @@
## Overview
libfm-qt is the Qt port of libfm, a library providing components to build desktop file managers which belongs to [LXDE](http://lxde.org).
libfm-qt is the Qt port of libfm, a library providing components to build
desktop file managers which belongs to [LXDE](https://lxde.org).
libfm-qt is licensed under the terms of the [LGPLv2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) or any later version. See file LICENSE for its full text.
libfm-qt is licensed under the terms of the
[LGPLv2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html)
or any later version. See file LICENSE for its full text.
## Installation
### Compiling source code
Runtime dependencies are Qt X11 Extras and libfm ≥ 1,2 (not all features are provided by libfm-qt yet).
Additional build dependencies are CMake, [lxqt-build-tools](https://github.com/lxde/lxqt-build-tools) and optionally Git to pull latest VCS checkouts. The localization files were outsourced to repository [lxqt-l10n](https://github.com/lxde/lxqt-l10n) so the corresponding dependencies are needed, too. Please refer to this repository's `README.md` for further information.
Runtime dependencies are Qt X11 Extras and libfm ≥ 1.2
(not all features are provided by libfm-qt yet).
Additional build dependencies are CMake,
[lxqt-build-tools](https://github.com/lxqt/lxqt-build-tools) and optionally Git
to pull latest VCS checkouts. The localization files were outsourced to
repository [lxqt-l10n](https://github.com/lxqt/lxqt-l10n) so the corresponding
dependencies are needed, too. Please refer to this repository's `README.md` for
further information.
Code configuration is handled by CMake. CMake variable `CMAKE_INSTALL_PREFIX` has to be set to `/usr` on most operating systems, depending on the way library paths are dealt with on 64bit systems variables like `CMAKE_INSTALL_LIBDIR` may have to be set as well.
Code configuration is handled by CMake. CMake variable `CMAKE_INSTALL_PREFIX`
has to be set to `/usr` on most operating systems, depending on the way library
paths are dealt with on 64bit systems variables like `CMAKE_INSTALL_LIBDIR` may
have to be set as well.
To build run `make`, to install `make install` which accepts variable `DESTDIR` as usual.
To build run `make`, to install `make install` which accepts variable `DESTDIR`
as usual.
### Binary packages
Official binary packages are available in Arch Linux, Debian (as of Debian stretch) and openSUSE (Leap 42.1 and Tumbleweed).
The library is still missing in Fedora which is providing version 0.10.0 of PCManFM-Qt only so far. This version was still including the code outsourced into libfm-qt later so libfm-qt will have to be provided by Fedora, too, as soon as the distribution upgrades to PCManFM-Qt ≥ 0.10.1.
Official binary packages are available in Arch Linux, Debian (as of Debian
stretch) and openSUSE (Leap 42.1 and Tumbleweed).
The library is still missing in Fedora which is providing version 0.10.0 of
PCManFM-Qt only so far. This version was still including the code outsourced
into libfm-qt later so libfm-qt will have to be provided by Fedora, too,
as soon as the distribution upgrades to PCManFM-Qt ≥ 0.10.1.
## Development
Issues should go to the tracker of PCManFM-Qt at https://github.com/lxde/pcmanfm-qt/issues.
Issues should go to the tracker of PCManFM-Qt at
https://github.com/lxqt/pcmanfm-qt/issues.

@ -75,3 +75,6 @@ desktop_id=terminology.desktop
open_arg=-e
noclose_arg=--hold -e
desktop_id=termite.desktop
[kitty]
desktop_id=kitty.desktop

11
debian/changelog vendored

@ -1,8 +1,15 @@
libfm-qt (0.13.0-1) experimental; urgency=medium
*
* Cherry-picking upstream release 0.13.0.
* Removed build dependency libglib2.0-dev, thrown in via lxqt-build-tools
* Bumped build dependency lxqt-build-tools to >= 0.5.0~
* Renamed libfm-qt3 -> libfm-qt5. soname bumped
* Added Breaks and replaces for libfm-qt3
* Bumped years in copyright
* Added symbols and removed two not used old (internal) ones, no soname bump
needed.
-- Alf Gaida <agaida@siduction.org> Mon, 21 May 2018 16:44:53 +0200
-- Alf Gaida <agaida@siduction.org> Wed, 23 May 2018 20:58:31 +0200
libfm-qt (0.12.0-17) unstable; urgency=medium

@ -1,4 +1,4 @@
libfm-qt.so.3 libfm-qt3 #MINVER#
libfm-qt.so.5 libfm-qt5 #MINVER#
(c++)"Fm::AppChooserComboBox::AppChooserComboBox(QWidget*)@Base" 0.10.0
(c++)"Fm::AppChooserComboBox::isChanged() const@Base" 0.12.0
(c++)"Fm::AppChooserComboBox::metaObject() const@Base" 0.10.0
@ -37,7 +37,35 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::AppMenuView::selectionChanged(QItemSelection const&, QItemSelection const&)@Base" 0.12.0
(c++)"Fm::AppMenuView::staticMetaObject@Base" 0.10.0
(c++)"Fm::AppMenuView::~AppMenuView()@Base" 0.10.0
(c++)"Fm::Archiver::Archiver()@Base" 0.13.0~
(c++)"Fm::Archiver::allArchivers()@Base" 0.13.0~
(c++)"Fm::Archiver::allArchivers_@Base" 0.13.0~
(c++)"Fm::Archiver::createArchive(_GAppLaunchContext*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&)@Base" 0.13.0~
(c++)"Fm::Archiver::defaultArchiver()@Base" 0.13.0~
(c++)"Fm::Archiver::defaultArchiver_@Base" 0.13.0~
(c++)"Fm::Archiver::extractArchives(_GAppLaunchContext*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&)@Base" 0.13.0~
(c++)"Fm::Archiver::extractArchivesTo(_GAppLaunchContext*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&, Fm::FilePath const&)@Base" 0.13.0~
(c++)"Fm::Archiver::isMimeTypeSupported(char const*)@Base" 0.13.0~
(c++)"Fm::Archiver::launchProgram(_GAppLaunchContext*, char const*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&, Fm::FilePath const&)@Base" 0.13.0~
(c++)"Fm::Archiver::setDefaultArchiver(Fm::Archiver*)@Base" 0.13.0~
(c++)"Fm::Archiver::setDefaultArchiverByName(char const*)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::BasicFileLauncher()@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::ask(char const*, char* const*, int)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::askExecFile(std::shared_ptr<Fm::FileInfo const> const&)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::chooseApp(Fm::FileInfoList const&, char const*, Fm::GErrorPtr&)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::handleShortcut(std::shared_ptr<Fm::FileInfo const> const&, _GAppLaunchContext*)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::launchDesktopEntry(char const*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&, _GAppLaunchContext*)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::launchDesktopEntry(std::shared_ptr<Fm::FileInfo const> const&, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&, _GAppLaunchContext*)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::launchExecutable(std::shared_ptr<Fm::FileInfo const> const&, _GAppLaunchContext*)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::launchFiles(Fm::FileInfoList const&, _GAppLaunchContext*)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::launchPaths(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, _GAppLaunchContext*)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::launchWithApp(_GAppInfo*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&, _GAppLaunchContext*)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::launchWithDefaultApp(std::shared_ptr<Fm::FileInfo const> const&, _GAppLaunchContext*)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::openFolder(_GAppLaunchContext*, Fm::FileInfoList const&, Fm::GErrorPtr&)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::showError(_GAppLaunchContext*, Fm::GErrorPtr&, Fm::FilePath const&, std::shared_ptr<Fm::FileInfo const> const&)@Base" 0.13.0~
(c++)"Fm::BasicFileLauncher::~BasicFileLauncher()@Base" 0.13.0~
(c++)"Fm::BookmarkAction::BookmarkAction(std::shared_ptr<Fm::BookmarkItem const>, QObject*)@Base" 0.12.0
(c++)"Fm::BookmarkItem::BookmarkItem(Fm::FilePath const&, QString)@Base" 0.13.0~
(c++)"Fm::Bookmarks::Bookmarks(QObject*)@Base" 0.12.0
(c++)"Fm::Bookmarks::changed()@Base" 0.12.0
(c++)"Fm::Bookmarks::globalInstance()@Base" 0.12.0
@ -82,30 +110,20 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::ColorButton::setColor(QColor const&)@Base" 0.10.0
(c++)"Fm::ColorButton::staticMetaObject@Base" 0.10.0
(c++)"Fm::ColorButton::~ColorButton()@Base" 0.10.0
(c++)"Fm::CopyJob::CopyJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&&, Fm::FilePath const&&, Fm::CopyJob::Mode)@Base" 0.12.0
(c++)"Fm::CopyJob::CopyJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&, Fm::FilePath const&, Fm::CopyJob::Mode)@Base" 0.12.0
(c++)"Fm::CopyJob::copyDir(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo>, Fm::FilePath const&)@Base" 0.12.0
(c++)"Fm::CopyJob::copyPath(Fm::FilePath const&, Fm::FilePath const&, char const*)@Base" 0.12.0
(c++)"Fm::CopyJob::copyPath(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, Fm::FilePath const&, char const*)@Base" 0.12.0
(c++)"Fm::CopyJob::copyRegularFile(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo>, Fm::FilePath const&)@Base" 0.12.0
(c++)"Fm::CopyJob::copySpecialFile(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo>, Fm::FilePath const&)@Base" 0.12.0
(c++)"Fm::CopyJob::exec()@Base" 0.12.0
(c++|arch= !armel !armhf !i386 !mips !mipsel !hppa !hurd-i386 !kfreebsd-i386 !m68k !powerpc !sh4 !x32 )"Fm::CopyJob::gfileProgressCallback(long, long, Fm::CopyJob*)@Base" 0.12.0
(c++|arch= !amd64 !arm64 !mips64el !ppc64el !s390x !alpha !ia64 !kfreebsd-amd64 !ppc64 !riscv64 !sparc64 )"Fm::CopyJob::gfileProgressCallback(long long, long long, Fm::CopyJob*)@Base" 0.12.0
(c++)"Fm::CopyJob::makeDir(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo>, Fm::FilePath const&)@Base" 0.12.0
(c++)"Fm::CopyJob::metaObject() const@Base" 0.12.0
(c++)"Fm::CopyJob::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.12.0
(c++)"Fm::CopyJob::qt_metacast(char const*)@Base" 0.12.0
(c++)"Fm::CopyJob::staticMetaObject@Base" 0.12.0
(c++)"Fm::CreateNewMenu::CreateNewMenu(QWidget*, Fm::FilePath, QWidget*)@Base" 0.12.0
(c++)"Fm::CreateNewMenu::addTemplateItem(std::shared_ptr<Fm::TemplateItem const> const&)@Base" 0.13.0~
(c++)"Fm::CreateNewMenu::metaObject() const@Base" 0.10.0
(c++)"Fm::CreateNewMenu::onCreateNew()@Base" 0.10.0
(c++)"Fm::CreateNewMenu::onCreateNewFile()@Base" 0.10.0
(c++)"Fm::CreateNewMenu::onCreateNewFolder()@Base" 0.10.0
(c++)"Fm::CreateNewMenu::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.10.0
(c++)"Fm::CreateNewMenu::qt_metacast(char const*)@Base" 0.10.0
(c++)"Fm::CreateNewMenu::removeTemplateItem(std::shared_ptr<Fm::TemplateItem const> const&)@Base" 0.13.0~
(c++)"Fm::CreateNewMenu::staticMetaObject@Base" 0.10.0
(c++)"Fm::CreateNewMenu::updateTemplateItem(std::shared_ptr<Fm::TemplateItem const> const&, std::shared_ptr<Fm::TemplateItem const> const&)@Base" 0.13.0~
(c++)"Fm::CreateNewMenu::~CreateNewMenu()@Base" 0.10.0
(c++)"Fm::DeleteJob::DeleteJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&)@Base" 0.13.0~
(c++)"Fm::DeleteJob::DeleteJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >&&)@Base" 0.13.0~
(c++)"Fm::DeleteJob::deleteDirContent(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo>)@Base" 0.12.0
(c++)"Fm::DeleteJob::deleteFile(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo>)@Base" 0.12.0
(c++)"Fm::DeleteJob::exec()@Base" 0.12.0
@ -113,6 +131,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::DeleteJob::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.12.0
(c++)"Fm::DeleteJob::qt_metacast(char const*)@Base" 0.12.0
(c++)"Fm::DeleteJob::staticMetaObject@Base" 0.12.0
(c++)"Fm::DeleteJob::~DeleteJob()@Base" 0.13.0~
(c++)"Fm::DirListJob::DirListJob(Fm::FilePath const&, Fm::DirListJob::Flags, std::shared_ptr<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > const> const&)@Base" 0.12.0
(c++)"Fm::DirListJob::exec()@Base" 0.12.0
(c++)"Fm::DirListJob::filesFound(Fm::FileInfoList&)@Base" 0.12.0
@ -206,8 +225,18 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::EditBookmarksDialog::qt_metacast(char const*)@Base" 0.10.0
(c++)"Fm::EditBookmarksDialog::staticMetaObject@Base" 0.10.0
(c++)"Fm::EditBookmarksDialog::~EditBookmarksDialog()@Base" 0.10.0
(c++)"Fm::FileChangeAttrJob::FileChangeAttrJob()@Base" 0.12.0
(c++)"Fm::FileChangeAttrJob::FileChangeAttrJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >)@Base" 0.13.0~
(c++)"Fm::FileChangeAttrJob::changeFileDisplayName(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, char const*)@Base" 0.13.0~
(c++)"Fm::FileChangeAttrJob::changeFileGroup(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, unsigned int)@Base" 0.13.0~
(c++)"Fm::FileChangeAttrJob::changeFileHidden(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, bool)@Base" 0.13.0~
(c++)"Fm::FileChangeAttrJob::changeFileIcon(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, Fm::GObjectPtr<_GIcon>&)@Base" 0.13.0~
(c++)"Fm::FileChangeAttrJob::changeFileMode(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, unsigned int, unsigned int)@Base" 0.13.0~
(c++)"Fm::FileChangeAttrJob::changeFileOwner(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, unsigned int)@Base" 0.13.0~
(c++)"Fm::FileChangeAttrJob::changeFileTargetUri(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, char const*)@Base" 0.13.0~
(c++)"Fm::FileChangeAttrJob::exec()@Base" 0.13.0~
(c++)"Fm::FileChangeAttrJob::handleError(Fm::GErrorPtr&, Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, Fm::Job::ErrorSeverity)@Base" 0.13.0~
(c++)"Fm::FileChangeAttrJob::metaObject() const@Base" 0.12.0
(c++)"Fm::FileChangeAttrJob::processFile(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&)@Base" 0.13.0~
(c++)"Fm::FileChangeAttrJob::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.12.0
(c++)"Fm::FileChangeAttrJob::qt_metacast(char const*)@Base" 0.12.0
(c++)"Fm::FileChangeAttrJob::staticMetaObject@Base" 0.12.0
@ -270,7 +299,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::FileInfo::isExecutableType() const@Base" 0.12.0
(c++)"Fm::FileInfo::setFromGFileInfo(Fm::GObjectPtr<_GFileInfo> const&, Fm::FilePath const&)@Base" 0.12.0
(c++)"Fm::FileInfo::~FileInfo()@Base" 0.12.0
(c++)"Fm::FileInfoJob::FileInfoJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, Fm::FilePath, std::shared_ptr<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > const> const&)@Base" 0.12.0
(c++)"Fm::FileInfoJob::FileInfoJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, Fm::FilePath, std::shared_ptr<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > const> const&)@Base" 0.13.0~
(c++)"Fm::FileInfoJob::exec()@Base" 0.12.0
(c++)"Fm::FileInfoJob::gotInfo(Fm::FilePath const&, std::shared_ptr<Fm::FileInfo const>&)@Base" 0.12.0
(c++)"Fm::FileInfoJob::metaObject() const@Base" 0.12.0
@ -281,15 +310,12 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::FileInfoList::isSameType() const@Base" 0.12.0
(c++)"Fm::FileLauncher::FileLauncher()@Base" 0.10.0
(c++)"Fm::FileLauncher::ask(char const*, char* const*, int)@Base" 0.10.0
(c++)"Fm::FileLauncher::error(_GAppLaunchContext*, _GError*, _FmPath*)@Base" 0.10.0
(c++)"Fm::FileLauncher::execFile(_FmFileInfo*)@Base" 0.10.0
(c++)"Fm::FileLauncher::funcs@Base" 0.10.0
(c++)"Fm::FileLauncher::getApp(_GList*, _FmMimeType*, _GError**)@Base" 0.10.0
(c++)"Fm::FileLauncher::launchFiles(QWidget*, Fm::FileInfoList)@Base" 0.12.0
(c++)"Fm::FileLauncher::launchFiles(QWidget*, _GList*)@Base" 0.10.0
(c++)"Fm::FileLauncher::launchPaths(QWidget*, _GList*)@Base" 0.10.0
(c++)"Fm::FileLauncher::launchPaths(QWidget*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >)@Base" 0.12.0
(c++)"Fm::FileLauncher::openFolder(_GAppLaunchContext*, _GList*, _GError**)@Base" 0.10.0
(c++)"Fm::FileLauncher::askExecFile(std::shared_ptr<Fm::FileInfo const> const&)@Base" 0.13.0~
(c++)"Fm::FileLauncher::chooseApp(Fm::FileInfoList const&, char const*, Fm::GErrorPtr&)@Base" 0.13.0~
(c++)"Fm::FileLauncher::launchFiles(QWidget*, Fm::FileInfoList const&)@Base" 0.13.0~
(c++)"Fm::FileLauncher::launchPaths(QWidget*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&)@Base" 0.13.0~
(c++)"Fm::FileLauncher::openFolder(_GAppLaunchContext*, Fm::FileInfoList const&, Fm::GErrorPtr&)@Base" 0.13.0~
(c++)"Fm::FileLauncher::showError(_GAppLaunchContext*, Fm::GErrorPtr&, Fm::FilePath const&, std::shared_ptr<Fm::FileInfo const> const&)@Base" 0.13.0~
(c++)"Fm::FileLauncher::~FileLauncher()@Base" 0.12.0
(c++)"Fm::FileLinkJob::FileLinkJob()@Base" 0.12.0
(c++)"Fm::FileMenu::FileMenu(Fm::FileInfoList, std::shared_ptr<Fm::FileInfo const>, Fm::FilePath, bool, QString const&, QWidget*)@Base" 0.12.0
@ -321,37 +347,41 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::FileMonitor::qt_metacast(char const*)@Base" 0.12.0
(c++)"Fm::FileMonitor::staticMetaObject@Base" 0.12.0
(c++)"Fm::FileOperation::FileOperation(Fm::FileOperation::Type, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, QObject*)@Base" 0.12.0
(c++)"Fm::FileOperation::cancel()@Base" 0.13.0~
(c++)"Fm::FileOperation::changeAttrFiles(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, QWidget*)@Base" 0.12.0
(c++)"Fm::FileOperation::copyFiles(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, Fm::FilePath, QWidget*)@Base" 0.12.0
(c++)"Fm::FileOperation::copyFiles(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, QWidget*)@Base" 0.13.0~
(c++)"Fm::FileOperation::deleteFiles(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, bool, QWidget*)@Base" 0.12.0
(c++)"Fm::FileOperation::disconnectJob()@Base" 0.10.0
(c++)"Fm::FileOperation::finished()@Base" 0.10.0
(c++)"Fm::FileOperation::handleFinish()@Base" 0.10.0
(c++)"Fm::FileOperation::metaObject() const@Base" 0.10.0
(c++)"Fm::FileOperation::moveFiles(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, Fm::FilePath, QWidget*)@Base" 0.12.0
(c++)"Fm::FileOperation::onFileOpsJobAsk(_FmFileOpsJob*, char const*, char* const*, Fm::FileOperation*)@Base" 0.10.0
(c++)"Fm::FileOperation::onFileOpsJobAskRename(_FmFileOpsJob*, _FmFileInfo*, _FmFileInfo*, char**, Fm::FileOperation*)@Base" 0.10.0
(c++)"Fm::FileOperation::onFileOpsJobCancelled(_FmFileOpsJob*, Fm::FileOperation*)@Base" 0.10.0
(c++)"Fm::FileOperation::onFileOpsJobCurFile(_FmFileOpsJob*, char const*, Fm::FileOperation*)@Base" 0.10.0
(c++)"Fm::FileOperation::onFileOpsJobError(_FmFileOpsJob*, _GError*, FmJobErrorSeverity, Fm::FileOperation*)@Base" 0.10.0
(c++)"Fm::FileOperation::onFileOpsJobFinished(_FmFileOpsJob*, Fm::FileOperation*)@Base" 0.10.0
(c++)"Fm::FileOperation::onFileOpsJobPercent(_FmFileOpsJob*, unsigned int, Fm::FileOperation*)@Base" 0.10.0
(c++)"Fm::FileOperation::onFileOpsJobPrepared(_FmFileOpsJob*, Fm::FileOperation*)@Base" 0.10.0
(c++)"Fm::FileOperation::moveFiles(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, QWidget*)@Base" 0.13.0~
(c++)"Fm::FileOperation::onJobCancalled()@Base" 0.13.0~
(c++)"Fm::FileOperation::onJobError(Fm::GErrorPtr const&, Fm::Job::ErrorSeverity, Fm::Job::ErrorAction&)@Base" 0.13.0~
(c++)"Fm::FileOperation::onJobFileExists(Fm::FileInfo const&, Fm::FileInfo const&, Fm::FileOperationJob::FileExistsAction&, Fm::FilePath&)@Base" 0.13.0~
(c++)"Fm::FileOperation::onJobFinish()@Base" 0.13.0~
(c++)"Fm::FileOperation::onJobPrepared()@Base" 0.13.0~
(c++)"Fm::FileOperation::onUiTimeout()@Base" 0.10.0
(c++)"Fm::FileOperation::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.10.0
(c++)"Fm::FileOperation::qt_metacast(char const*)@Base" 0.10.0
(c++)"Fm::FileOperation::run()@Base" 0.10.0
(c++)"Fm::FileOperation::setChmod(unsigned int, unsigned int)@Base" 0.13.0~
(c++)"Fm::FileOperation::setChown(unsigned int, unsigned int)@Base" 0.13.0~
(c++)"Fm::FileOperation::setDestFiles(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >)@Base" 0.13.0~
(c++)"Fm::FileOperation::setDestination(Fm::FilePath)@Base" 0.12.0
(c++)"Fm::FileOperation::setRecursiveChattr(bool)@Base" 0.13.0~
(c++)"Fm::FileOperation::showDialog()@Base" 0.10.0
(c++)"Fm::FileOperation::staticMetaObject@Base" 0.10.0
(c++)"Fm::FileOperation::symlinkFiles(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, Fm::FilePath, QWidget*)@Base" 0.12.0
(c++)"Fm::FileOperation::symlinkFiles(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, QWidget*)@Base" 0.13.0~
(c++)"Fm::FileOperation::trashFiles(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, bool, QWidget*)@Base" 0.12.0
(c++)"Fm::FileOperation::unTrashFiles(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, QWidget*)@Base" 0.12.0
(c++)"Fm::FileOperation::~FileOperation()@Base" 0.10.0
(c++)"Fm::FileOperationDialog::FileOperationDialog(Fm::FileOperation*)@Base" 0.10.0
(c++)"Fm::FileOperationDialog::ask(QString, char* const*)@Base" 0.10.0
(c++)"Fm::FileOperationDialog::askRename(_FmFileInfo*, _FmFileInfo*, QString&)@Base" 0.10.0
(c++)"Fm::FileOperationDialog::error(_GError*, FmJobErrorSeverity)@Base" 0.10.0
(c++)"Fm::FileOperationDialog::askRename(Fm::FileInfo const&, Fm::FileInfo const&, Fm::FilePath&)@Base" 0.13.0~
(c++)"Fm::FileOperationDialog::error(_GError*, Fm::Job::ErrorSeverity)@Base" 0.13.0~
(c++)"Fm::FileOperationDialog::metaObject() const@Base" 0.10.0
(c++)"Fm::FileOperationDialog::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.10.0
(c++)"Fm::FileOperationDialog::qt_metacast(char const*)@Base" 0.10.0
@ -360,6 +390,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++|arch= !armel !armhf !i386 !mips !mipsel !hppa !hurd-i386 !kfreebsd-i386 !m68k !powerpc !sh4 !x32 )"Fm::FileOperationDialog::setDataTransferred(unsigned long, unsigned long)@Base" 0.12.0
(c++|arch= !amd64 !arm64 !mips64el !ppc64el !s390x !alpha !ia64 !kfreebsd-amd64 !ppc64 !ppc64 !riscv64 !sparc64 )"Fm::FileOperationDialog::setDataTransferred(unsigned long long, unsigned long long)@Base" 0.12.0
(c++)"Fm::FileOperationDialog::setDestPath(Fm::FilePath const&)@Base" 0.12.0
(optional|c++|arch= !i386 )"Fm::FileOperationDialog::setFilesProcessed(unsigned long, unsigned long)@Base" 0.13.0~
(c++)"Fm::FileOperationDialog::setPercent(unsigned int)@Base" 0.10.0
(c++)"Fm::FileOperationDialog::setPrepared()@Base" 0.10.0
(c++)"Fm::FileOperationDialog::setRemainingTime(unsigned int)@Base" 0.10.0
@ -370,6 +401,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++|arch= !armel !armhf !i386 !mips !mipsel !hppa !hurd-i386 !kfreebsd-i386 !m68k !powerpc !sh4 !x32 )"Fm::FileOperationJob::addFinishedAmount(unsigned long, unsigned long)@Base" 0.12.0
(c++|arch= !amd64 !arm64 !mips64el !ppc64el !s390x !alpha !ia64 !kfreebsd-amd64 !ppc64 !riscv64 !sparc64 )"Fm::FileOperationJob::addFinishedAmount(unsigned long long, unsigned long long)@Base" 0.12.0
(c++)"Fm::FileOperationJob::askRename(Fm::FileInfo const&, Fm::FileInfo const&, Fm::FilePath&)@Base" 0.12.0
(c++)"Fm::FileOperationJob::currentFile() const@Base" 0.13.0~
(c++|arch= !armel !armhf !i386 !mips !mipsel !hppa !hurd-i386 !kfreebsd-i386 !m68k !powerpc !sh4 !x32 )"Fm::FileOperationJob::currentFileProgress(Fm::FilePath&, unsigned long&, unsigned long&) const@Base" 0.12.0
(c++|arch= !amd64 !arm64 !mips64el !ppc64el !s390x !alpha !ia64 !kfreebsd-amd64 !ppc64 !riscv64 !sparc64 )"Fm::FileOperationJob::currentFileProgress(Fm::FilePath&, unsigned long long&, unsigned long long&) const@Base" 0.12.0
(c++)"Fm::FileOperationJob::fileExists(Fm::FileInfo const&, Fm::FileInfo const&, Fm::FileOperationJob::FileExistsAction&, Fm::FilePath&)@Base" 0.12.0
@ -377,6 +409,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++|arch= !amd64 !arm64 !mips64el !ppc64el !s390x !alpha !ia64 !kfreebsd-amd64 !ppc64 !riscv64 !sparc64 )"Fm::FileOperationJob::finishedAmount(unsigned long long&, unsigned long long&) const@Base" 0.12.0
(c++)"Fm::FileOperationJob::metaObject() const@Base" 0.12.0
(c++)"Fm::FileOperationJob::preparedToRun()@Base" 0.12.0
(c++)"Fm::FileOperationJob::progress() const@Base" 0.13.0~
(c++)"Fm::FileOperationJob::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.12.0
(c++)"Fm::FileOperationJob::qt_metacast(char const*)@Base" 0.12.0
(c++)"Fm::FileOperationJob::setCurrentFile(Fm::FilePath const&)@Base" 0.12.0
@ -427,6 +460,30 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::FileSystemInfoJob::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.12.0
(c++)"Fm::FileSystemInfoJob::qt_metacast(char const*)@Base" 0.12.0
(c++)"Fm::FileSystemInfoJob::staticMetaObject@Base" 0.12.0
(c++)"Fm::FileTransferJob::FileTransferJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, Fm::FilePath const&, Fm::FileTransferJob::Mode)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::FileTransferJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, Fm::FileTransferJob::Mode)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::FileTransferJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >, Fm::FileTransferJob::Mode)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::copyDirContent(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo>, Fm::FilePath&, bool)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::copyFile(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, Fm::FilePath const&, char const*, bool)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::copyRegularFile(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, Fm::FilePath&)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::copySpecialFile(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, Fm::FilePath&)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::createShortcut(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, Fm::FilePath&)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::createSymlink(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, Fm::FilePath&)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::exec()@Base" 0.13.0~
(optional|c++|arch= !i386 )"Fm::FileTransferJob::gfileCopyProgressCallback(long, long, Fm::FileTransferJob*)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::handleError(Fm::GErrorPtr&, Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, Fm::FilePath&, int&)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::linkFile(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, Fm::FilePath const&, char const*)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::makeDir(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo>, Fm::FilePath&)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::metaObject() const@Base" 0.13.0~
(c++)"Fm::FileTransferJob::moveFile(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, Fm::FilePath const&, char const*)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::moveFileSameFs(Fm::FilePath const&, Fm::GObjectPtr<_GFileInfo> const&, Fm::FilePath&)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::processPath(Fm::FilePath const&, Fm::FilePath const&, char const*)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::qt_metacast(char const*)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::setDestDirPath(Fm::FilePath const&)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::setDestPaths(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::setSrcPaths(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >)@Base" 0.13.0~
(c++)"Fm::FileTransferJob::staticMetaObject@Base" 0.13.0~
(c++)"Fm::Folder::Folder()@Base" 0.12.0
(c++)"Fm::Folder::Folder(Fm::FilePath const&)@Base" 0.12.0
(c++)"Fm::Folder::cache_@Base" 0.12.0
@ -522,6 +579,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::FolderModel::dropMimeData(QMimeData const*, Qt::DropAction, int, int, QModelIndex const&)@Base" 0.10.0
(c++)"Fm::FolderModel::fileInfoFromIndex(QModelIndex const&) const@Base" 0.10.0
(c++)"Fm::FolderModel::fileSizeChanged(QModelIndex const&)@Base" 0.12.0
(c++)"Fm::FolderModel::filesAdded(Fm::FileInfoList)@Base" 0.13.0~
(c++)"Fm::FolderModel::findItemByFileInfo(Fm::FileInfo const*, int*)@Base" 0.12.0
(c++)"Fm::FolderModel::findItemByName(char const*, int*)@Base" 0.10.0
(c++)"Fm::FolderModel::findItemByPath(Fm::FilePath const&, int*)@Base" 0.12.0
@ -597,8 +655,10 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::FolderView::prepareFolderMenu(Fm::FolderMenu*)@Base" 0.10.0
(c++)"Fm::FolderView::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.10.0
(c++)"Fm::FolderView::qt_metacast(char const*)@Base" 0.10.0
(c++)"Fm::FolderView::scrollSmoothly()@Base" 0.13.0~
(c++)"Fm::FolderView::selChanged()@Base" 0.12.0
(c++)"Fm::FolderView::selectAll()@Base" 0.10.0
(c++)"Fm::FolderView::selectFiles(Fm::FileInfoList const&, bool)@Base" 0.13.0~
(c++)"Fm::FolderView::selectedFilePaths() const@Base" 0.10.0
(c++)"Fm::FolderView::selectedFiles() const@Base" 0.10.0
(c++)"Fm::FolderView::selectedIndexes() const@Base" 0.10.0
@ -627,25 +687,15 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::IconInfo::IconInfo(char const*)@Base" 0.12.0
(c++)"Fm::IconInfo::cache_@Base" 0.12.0
(c++)"Fm::IconInfo::emblems() const@Base" 0.12.0
(c++)"Fm::IconInfo::fallbackQicon_@Base" 0.12.0
(c++)"Fm::IconInfo::fallbackQicons_@Base" 0.13.0~
(c++)"Fm::IconInfo::fromGIcon(Fm::GObjectPtr<_GIcon>)@Base" 0.12.0
(c++)"Fm::IconInfo::fromName(char const*)@Base" 0.12.0
(c++)"Fm::IconInfo::internalQicon() const@Base" 0.12.0
(c++)"Fm::IconInfo::mutex_@Base" 0.12.0
(c++)"Fm::IconInfo::qicon(bool const&) const@Base" 0.12.0
(c++)"Fm::IconInfo::qiconFromNames(char const* const*)@Base" 0.12.0
(c++)"Fm::IconInfo::qiconsFromNames(char const* const*)@Base" 0.13.0~
(c++)"Fm::IconInfo::updateQIcons()@Base" 0.12.0
(c++)"Fm::IconInfo::~IconInfo()@Base" 0.12.0
(c++)"Fm::IconTheme::IconTheme()@Base" 0.10.0
(c++)"Fm::IconTheme::changed()@Base" 0.10.0
(c++)"Fm::IconTheme::checkChanged()@Base" 0.10.0
(c++)"Fm::IconTheme::eventFilter(QObject*, QEvent*)@Base" 0.10.0
(c++)"Fm::IconTheme::instance()@Base" 0.10.0
(c++)"Fm::IconTheme::metaObject() const@Base" 0.10.0
(c++)"Fm::IconTheme::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.10.0
(c++)"Fm::IconTheme::qt_metacast(char const*)@Base" 0.10.0
(c++)"Fm::IconTheme::staticMetaObject@Base" 0.10.0
(c++)"Fm::IconTheme::~IconTheme()@Base" 0.12.0
(c++)"Fm::Job::Job()@Base" 0.12.0
(c++)"Fm::Job::cancel()@Base" 0.12.0
(c++)"Fm::Job::cancelled()@Base" 0.12.0
@ -676,12 +726,15 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::MountOperation::finished(_GError*)@Base" 0.10.0
(c++)"Fm::MountOperation::handleFinish(_GError*)@Base" 0.10.0
(c++)"Fm::MountOperation::metaObject() const@Base" 0.10.0
(c++)"Fm::MountOperation::mountEnclosingVolume(Fm::FilePath const&)@Base" 0.13.0~
(c++)"Fm::MountOperation::mountMountable(Fm::FilePath const&)@Base" 0.13.0~
(c++)"Fm::MountOperation::onAbort(_GMountOperation*, Fm::MountOperation*)@Base" 0.10.0
(c++)"Fm::MountOperation::onAskPassword(_GMountOperation*, char*, char*, char*, GAskPasswordFlags, Fm::MountOperation*)@Base" 0.10.0
(c++)"Fm::MountOperation::onAskQuestion(_GMountOperation*, char*, char**, Fm::MountOperation*)@Base" 0.10.0
(c++)"Fm::MountOperation::onEjectMountFinished(_GMount*, _GAsyncResult*, QPointer<Fm::MountOperation>*)@Base" 0.10.0
(c++)"Fm::MountOperation::onEjectVolumeFinished(_GVolume*, _GAsyncResult*, QPointer<Fm::MountOperation>*)@Base" 0.10.0
(c++)"Fm::MountOperation::onMountFileFinished(_GFile*, _GAsyncResult*, QPointer<Fm::MountOperation>*)@Base" 0.10.0
(c++)"Fm::MountOperation::onMountMountableFinished(_GFile*, _GAsyncResult*, QPointer<Fm::MountOperation>*)@Base" 0.13.0~
(c++)"Fm::MountOperation::onMountVolumeFinished(_GVolume*, _GAsyncResult*, QPointer<Fm::MountOperation>*)@Base" 0.10.0
(c++)"Fm::MountOperation::onShowProcesses(_GMountOperation*, char*, _GArray*, char**, Fm::MountOperation*)@Base" 0.10.0
(c++|arch= !armel !armhf !i386 !mips !mipsel !hppa !hurd-i386 !kfreebsd-i386 !m68k !powerpc !sh4 !x32 )"Fm::MountOperation::onShowUnmountProgress(_GMountOperation*, char*, long, long, Fm::MountOperation*)@Base" 0.12.0
@ -780,6 +833,16 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::PlacesModelVolumeItem::PlacesModelVolumeItem(_GVolume*)@Base" 0.10.0
(c++)"Fm::PlacesModelVolumeItem::isMounted()@Base" 0.10.0
(c++)"Fm::PlacesModelVolumeItem::update()@Base" 0.10.0
(c++)"Fm::PlacesProxyModel::PlacesProxyModel(QObject*)@Base" 0.13.0~
(c++)"Fm::PlacesProxyModel::filterAcceptsRow(int, QModelIndex const&) const@Base" 0.13.0~
(c++)"Fm::PlacesProxyModel::metaObject() const@Base" 0.13.0~
(c++)"Fm::PlacesProxyModel::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.13.0~
(c++)"Fm::PlacesProxyModel::qt_metacast(char const*)@Base" 0.13.0~
(c++)"Fm::PlacesProxyModel::restoreHiddenItems(QSet<QString> const&)@Base" 0.13.0~
(c++)"Fm::PlacesProxyModel::setHidden(QString const&, bool)@Base" 0.13.0~
(c++)"Fm::PlacesProxyModel::showAll(bool)@Base" 0.13.0~
(c++)"Fm::PlacesProxyModel::staticMetaObject@Base" 0.13.0~
(c++)"Fm::PlacesProxyModel::~PlacesProxyModel()@Base" 0.13.0~
(c++)"Fm::PlacesView::PlacesView(QWidget*)@Base" 0.10.0
(c++)"Fm::PlacesView::activateRow(int, QModelIndex const&)@Base" 0.10.0
(c++)"Fm::PlacesView::chdirRequested(int, Fm::FilePath const&)@Base" 0.12.0
@ -787,6 +850,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::PlacesView::contextMenuEvent(QContextMenuEvent*)@Base" 0.10.0
(c++)"Fm::PlacesView::dragMoveEvent(QDragMoveEvent*)@Base" 0.10.0
(c++)"Fm::PlacesView::dropEvent(QDropEvent*)@Base" 0.10.0
(c++)"Fm::PlacesView::hiddenItemSet(QString const&, bool)@Base" 0.13.0~
(c++)"Fm::PlacesView::metaObject() const@Base" 0.10.0
(c++)"Fm::PlacesView::onClicked(QModelIndex const&)@Base" 0.10.0
(c++)"Fm::PlacesView::onDeleteBookmark()@Base" 0.10.0
@ -803,9 +867,12 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::PlacesView::onRenameBookmark()@Base" 0.10.0
(c++)"Fm::PlacesView::onUnmountMount()@Base" 0.10.0
(c++)"Fm::PlacesView::onUnmountVolume()@Base" 0.10.0
(c++)"Fm::PlacesView::proxyModel_@Base" 0.13.0~
(c++)"Fm::PlacesView::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.10.0
(c++)"Fm::PlacesView::qt_metacast(char const*)@Base" 0.10.0
(c++)"Fm::PlacesView::restoreHiddenItems(QSet<QString> const&)@Base" 0.13.0~
(c++)"Fm::PlacesView::setCurrentPath(Fm::FilePath)@Base" 0.12.0
(c++)"Fm::PlacesView::showAll(bool)@Base" 0.13.0~
(c++)"Fm::PlacesView::staticMetaObject@Base" 0.10.0
(c++)"Fm::PlacesView::~PlacesView()@Base" 0.10.0
(c++)"Fm::ProxyFolderModel::ProxyFolderModel(QObject*)@Base" 0.10.0
@ -821,6 +888,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::ProxyFolderModel::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.10.0
(c++)"Fm::ProxyFolderModel::qt_metacast(char const*)@Base" 0.10.0
(c++)"Fm::ProxyFolderModel::removeFilter(Fm::ProxyFolderModelFilter*)@Base" 0.10.0
(c++)"Fm::ProxyFolderModel::setBackupAsHidden(bool)@Base" 0.13.0~
(c++)"Fm::ProxyFolderModel::setCutFiles(QItemSelection const&)@Base" 0.12.0
(c++)"Fm::ProxyFolderModel::setFolderFirst(bool)@Base" 0.10.0
(c++)"Fm::ProxyFolderModel::setShowHidden(bool)@Base" 0.10.0
@ -833,7 +901,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::ProxyFolderModel::staticMetaObject@Base" 0.10.0
(c++)"Fm::ProxyFolderModel::updateFilters()@Base" 0.10.0
(c++)"Fm::ProxyFolderModel::~ProxyFolderModel()@Base" 0.10.0
(c++)"Fm::RenameDialog::RenameDialog(_FmFileInfo*, _FmFileInfo*, QWidget*, QFlags<Qt::WindowType>)@Base" 0.10.0
(c++)"Fm::RenameDialog::RenameDialog(Fm::FileInfo const&, Fm::FileInfo const&, QWidget*, QFlags<Qt::WindowType>)@Base" 0.13.0~
(c++)"Fm::RenameDialog::accept()@Base" 0.10.0
(c++)"Fm::RenameDialog::metaObject() const@Base" 0.10.0
(c++)"Fm::RenameDialog::onFileNameChanged(QString)@Base" 0.10.0
@ -847,6 +915,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::SidePane::SidePane(QWidget*)@Base" 0.10.0
(c++)"Fm::SidePane::chdirRequested(int, Fm::FilePath const&)@Base" 0.12.0
(c++)"Fm::SidePane::createNewFolderRequested(Fm::FilePath const&)@Base" 0.12.0
(c++)"Fm::SidePane::hiddenPlaceSet(QString const&, bool)@Base" 0.13.0~
(c++)"Fm::SidePane::initDirTree()@Base" 0.10.0
(c++)"Fm::SidePane::metaObject() const@Base" 0.10.0
(c++)"Fm::SidePane::modeByName(char const*)@Base" 0.10.0
@ -859,6 +928,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::SidePane::prepareFileMenu(Fm::FileMenu*)@Base" 0.10.0
(c++)"Fm::SidePane::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.10.0
(c++)"Fm::SidePane::qt_metacast(char const*)@Base" 0.10.0
(c++)"Fm::SidePane::restoreHiddenPlaces(QSet<QString> const&)@Base" 0.13.0~
(c++)"Fm::SidePane::setCurrentPath(Fm::FilePath)@Base" 0.12.0
(c++)"Fm::SidePane::setHomeDir(char const*)@Base" 0.10.0
(c++)"Fm::SidePane::setIconSize(QSize)@Base" 0.10.0
@ -866,6 +936,23 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::SidePane::setShowHidden(bool)@Base" 0.10.0
(c++)"Fm::SidePane::staticMetaObject@Base" 0.10.0
(c++)"Fm::SidePane::~SidePane()@Base" 0.10.0
(c++)"Fm::TemplateItem::TemplateItem(std::shared_ptr<Fm::FileInfo const>)@Base" 0.13.0~
(c++)"Fm::TemplateItem::filePath() const@Base" 0.13.0~
(c++)"Fm::Templates::Templates()@Base" 0.13.0~
(c++)"Fm::Templates::addTemplateDir(char const*)@Base" 0.13.0~
(c++)"Fm::Templates::globalInstance()@Base" 0.13.0~
(c++)"Fm::Templates::globalInstance_@Base" 0.13.0~
(c++)"Fm::Templates::itemAdded(std::shared_ptr<Fm::TemplateItem const> const&)@Base" 0.13.0~
(c++)"Fm::Templates::itemChanged(std::shared_ptr<Fm::TemplateItem const> const&, std::shared_ptr<Fm::TemplateItem const> const&)@Base" 0.13.0~
(c++)"Fm::Templates::itemRemoved(std::shared_ptr<Fm::TemplateItem const> const&)@Base" 0.13.0~
(c++)"Fm::Templates::metaObject() const@Base" 0.13.0~
(c++)"Fm::Templates::onFilesAdded(Fm::FileInfoList&)@Base" 0.13.0~
(c++)"Fm::Templates::onFilesChanged(std::vector<std::pair<std::shared_ptr<Fm::FileInfo const>, std::shared_ptr<Fm::FileInfo const> >, std::allocator<std::pair<std::shared_ptr<Fm::FileInfo const>, std::shared_ptr<Fm::FileInfo const> > > >&)@Base" 0.13.0~
(c++)"Fm::Templates::onFilesRemoved(Fm::FileInfoList&)@Base" 0.13.0~
(c++)"Fm::Templates::onTemplateDirRemoved()@Base" 0.13.0~
(c++)"Fm::Templates::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.13.0~
(c++)"Fm::Templates::qt_metacast(char const*)@Base" 0.13.0~
(c++)"Fm::Templates::staticMetaObject@Base" 0.13.0~
(c++)"Fm::ThumbnailJob::ThumbnailJob(Fm::FileInfoList, int)@Base" 0.12.0
(c++)"Fm::ThumbnailJob::exec()@Base" 0.12.0
(c++)"Fm::ThumbnailJob::generateThumbnail(std::shared_ptr<Fm::FileInfo const> const&, Fm::FilePath const&, char const*, QString const&)@Base" 0.12.0
@ -898,15 +985,13 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::TotalSizeJob::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.12.0
(c++)"Fm::TotalSizeJob::qt_metacast(char const*)@Base" 0.12.0
(c++)"Fm::TotalSizeJob::staticMetaObject@Base" 0.12.0
(c++)"Fm::TrashJob::TrashJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&&)@Base" 0.12.0
(c++)"Fm::TrashJob::TrashJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&)@Base" 0.12.0
(c++)"Fm::TrashJob::TrashJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >)@Base" 0.13.0~
(c++)"Fm::TrashJob::exec()@Base" 0.12.0
(c++)"Fm::TrashJob::metaObject() const@Base" 0.12.0
(c++)"Fm::TrashJob::qt_metacall(QMetaObject::Call, int, void**)@Base" 0.12.0
(c++)"Fm::TrashJob::qt_metacast(char const*)@Base" 0.12.0
(c++)"Fm::TrashJob::staticMetaObject@Base" 0.12.0
(c++)"Fm::UntrashJob::UntrashJob()@Base" 0.12.0
(c++)"Fm::UntrashJob::ensure_parent_dir(_GFile*)@Base" 0.12.0
(c++)"Fm::UntrashJob::UntrashJob(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >)@Base" 0.13.0~
(c++)"Fm::UntrashJob::exec()@Base" 0.12.0
(c++)"Fm::UserInfoCache::UserInfoCache()@Base" 0.12.0
(c++)"Fm::UserInfoCache::changed()@Base" 0.12.0
@ -943,9 +1028,9 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"Fm::VolumeManager::volumeRemoved(Fm::Volume const&)@Base" 0.12.0
(c++)"Fm::VolumeManager::~VolumeManager()@Base" 0.12.0
(c++)"Fm::allKnownTerminals()@Base" 0.12.0
(c++)"Fm::changeFileName(Fm::FilePath const&, QString const&, QWidget*)@Base" 0.12.0
(c++)"Fm::changeFileName(Fm::FilePath const&, QString const&, QWidget*, bool)@Base" 0.13.0~
(c++)"Fm::copyFilesToClipboard(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&)@Base" 0.12.0
(c++)"Fm::createFileOrFolder(Fm::CreateFileType, Fm::FilePath, _FmTemplate*, QWidget*)@Base" 0.12.0
(c++)"Fm::createFileOrFolder(Fm::CreateFileType, Fm::FilePath, Fm::TemplateItem const*, QWidget*)@Base" 0.13.0~
(c++)"Fm::cutFilesToClipboard(std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > const&)@Base" 0.12.0
(c++)"Fm::execModelessDialog(QDialog*)@Base" 0.10.0
(c++|arch= !armel !armhf !i386 !mips !mipsel !hppa !hurd-i386 !kfreebsd-i386 !m68k !powerpc !sh4 !x32 )"Fm::formatFileSize(unsigned long, bool)@Base" 0.12.0
@ -969,6 +1054,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"non-virtual thunk to Fm::AppMenuView::~AppMenuView()@Base" 0.10.0
(c++)"non-virtual thunk to Fm::ColorButton::~ColorButton()@Base" 0.10.0
(c++)"non-virtual thunk to Fm::CreateNewMenu::~CreateNewMenu()@Base" 0.10.0
(c++)"non-virtual thunk to Fm::DeleteJob::~DeleteJob()@Base" 0.13.0~
(c++)"non-virtual thunk to Fm::DirTreeView::~DirTreeView()@Base" 0.10.0
(c++)"non-virtual thunk to Fm::EditBookmarksDialog::~EditBookmarksDialog()@Base" 0.10.0
(c++)"non-virtual thunk to Fm::FileDialog::~FileDialog()@Base" 0.12.0
@ -988,14 +1074,15 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"non-virtual thunk to Fm::ThumbnailJob::~ThumbnailJob()@Base" 0.12.0
(c++|arch= !armel !armhf !i386 !mips !mipsel !hppa !hurd-i386 !kfreebsd-i386 !m68k !powerpc !sh4 !x32 )"std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> > >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const>, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_find_before_node(unsigned long, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned long) const@Base" 0.12.0
(c++|arch= !amd64 !arm64 !mips64el !ppc64el !s390x !alpha !ia64 !kfreebsd-amd64 !ppc64 !riscv64 !sparc64 )"std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> > >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const>, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_find_before_node(unsigned int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, unsigned int) const@Base" 0.12.0
(c++|arch= !armel !armhf !i386 !mips !mipsel !hppa !hurd-i386 !kfreebsd-i386 !m68k !powerpc !sh4 !x32 )"std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> > >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const>, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_insert_unique_node(unsigned long, unsigned long, std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> >, true>*)@Base" 0.12.0
(c++|arch= !amd64 !arm64 !mips64el !ppc64el !s390x !alpha !ia64 !kfreebsd-amd64 !ppc64 !riscv64 !sparc64 )"std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> > >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const>, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_insert_unique_node(unsigned int, unsigned int, std::__detail::_Hash_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> >, true>*)@Base" 0.12.0
(c++)"std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> > >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const>, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::clear()@Base" 0.12.0
(optional=gcc8|c++|arch= !i386 )"std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> > >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const>, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::erase(std::__detail::_Node_const_iterator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> >, false, true>)@Base" 0.13.0~
(c++)"std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> > >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const>, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::find(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.12.0
(c++|arch= !armel !armhf !i386 !mips !mipsel !hppa !hurd-i386 !kfreebsd-i386 !m68k !powerpc !sh4 !x32 )"std::_Hashtable<unsigned int, std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> >, std::allocator<std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> > >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_insert_unique_node(unsigned long, unsigned long, std::__detail::_Hash_node<std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> >, false>*)@Base" 0.12.0
(c++|arch= !amd64 !arm64 !mips64el !ppc64el !s390x !alpha !ia64 !kfreebsd-amd64 !ppc64 !riscv64 !sparc64 )"std::_Hashtable<unsigned int, std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> >, std::allocator<std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> > >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_insert_unique_node(unsigned int, unsigned int, std::__detail::_Hash_node<std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> >, false>*)@Base" 0.12.0
(c++|arch= !armel !armhf !i386 !mips !mipsel !hppa !hurd-i386 !kfreebsd-i386 !m68k !powerpc !sh4 !x32 )"std::_Hashtable<unsigned int, std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> >, std::allocator<std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> > >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_insert_unique_node(unsigned long, unsigned long, std::__detail::_Hash_node<std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> >, false>*)@Base" 0.12.0
(c++|arch= !amd64 !arm64 !mips64el !ppc64el !s390x !alpha !ia64 !kfreebsd-amd64 !ppc64 !riscv64 !sparc64 )"std::_Hashtable<unsigned int, std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> >, std::allocator<std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> > >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_insert_unique_node(unsigned int, unsigned int, std::__detail::_Hash_node<std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> >, false>*)@Base" 0.12.0
(optional=gcc8|c++|arch= !i386 )"std::_Hashtable<unsigned int, std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> >, std::allocator<std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> > >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_insert_unique_node(unsigned long, unsigned long, std::__detail::_Hash_node<std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> >, false>*, unsigned long)@Base" 0.13.0~
(optional=gcc8|c++)"std::_Hashtable<unsigned int, std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> >, std::allocator<std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> > >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_insert_unique_node(unsigned long, unsigned long, std::__detail::_Hash_node<std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> >, false>*, unsigned long)@Base" 0.13.0~
(optional=gcc7|c++|arch= !armel !armhf !i386 !mips !mipsel !hppa !hurd-i386 !kfreebsd-i386 !m68k !powerpc !sh4 !x32 )"std::_Hashtable<unsigned int, std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> >, std::allocator<std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> > >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_insert_unique_node(unsigned long, unsigned long, std::__detail::_Hash_node<std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> >, false>*)@Base" 0.12.0
(optional=gcc7|c++|arch= !amd64 !arm64 !mips64el !ppc64el !s390x !alpha !ia64 !kfreebsd-amd64 !ppc64 !riscv64 !sparc64 )"std::_Hashtable<unsigned int, std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> >, std::allocator<std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> > >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_insert_unique_node(unsigned int, unsigned int, std::__detail::_Hash_node<std::pair<unsigned int const, std::shared_ptr<Fm::GroupInfo const> >, false>*)@Base" 0.12.0
(optional=gcc7|c++|arch= !armel !armhf !i386 !mips !mipsel !hppa !hurd-i386 !kfreebsd-i386 !m68k !powerpc !sh4 !x32 )"std::_Hashtable<unsigned int, std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> >, std::allocator<std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> > >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_insert_unique_node(unsigned long, unsigned long, std::__detail::_Hash_node<std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> >, false>*)@Base" 0.12.0
(optional=gcc7|c++|arch= !amd64 !arm64 !mips64el !ppc64el !s390x !alpha !ia64 !kfreebsd-amd64 !ppc64 !riscv64 !sparc64 )"std::_Hashtable<unsigned int, std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> >, std::allocator<std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> > >, std::__detail::_Select1st, std::equal_to<unsigned int>, std::hash<unsigned int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >::_M_insert_unique_node(unsigned int, unsigned int, std::__detail::_Hash_node<std::pair<unsigned int const, std::shared_ptr<Fm::UserInfo const> >, false>*)@Base" 0.12.0
(c++)"std::_Rb_tree<unsigned int, unsigned int, std::_Identity<unsigned int>, std::less<unsigned int>, std::allocator<unsigned int> >::_M_erase(std::_Rb_tree_node<unsigned int>*)@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)1>::_M_destroy()@Base" 0.12.0
@ -1074,78 +1161,62 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::MimeType, std::allocator<Fm::MimeType>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::MimeType, std::allocator<Fm::MimeType>, (__gnu_cxx::_Lock_policy)1>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::MimeType, std::allocator<Fm::MimeType>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::MimeType, std::allocator<Fm::MimeType>, (__gnu_cxx::_Lock_policy)1>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)1>::_M_destroy()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)1>::_M_dispose()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)1>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)1>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)1>::_M_destroy()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)1>::_M_dispose()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)1>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)1>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)1>::_M_destroy()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)1>::_M_dispose()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)1>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)1>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)1>::_M_destroy()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)1>::_M_dispose()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)1>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)1>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)1>::_M_destroy()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)1>::_M_dispose()@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)1>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++|arch= !armel !riscv64 )"std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++|arch= armel riscv64 )"std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)1>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++|arch= !arm64 !armel !armhf !i386 !mips !mipsel !mips64el !ppc64el !alpha !hppa !hurd-i386 !ia64 !kfreebsd-i386 !m68k !powerpc !ppc64 !riscv64 !sh4 !sparc64 !x32 )"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > __gnu_cxx::__to_xstring<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>(int (*)(char*, unsigned long, char const*, __va_list_tag*), unsigned long, char const*, ...)@Base" 0.12.0
(optional|c++|arch= !amd64 !arm64 !armel !armhf !i386 !mips !mips64el !mipsel !ppc64el !s390x !hppa !ia64 !kfreebsd-amd64 !kfreebsd-i386 !m68k !powerpc !ppc64 !riscv64 !sh4 !x32 )"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > __gnu_cxx::__to_xstring<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>(int (*)(char*, unsigned long, char const*, __va_list_tag), unsigned long, char const*, ...)@Base" 0.12.0
(optional|c++|arch= !amd64 !arm64 !i386 !mips !mips64el !mipsel !ppc64el !s390x !alpha !hppa !hurd-i386 !ia64 !kfreebsd-amd64 !kfreebsd-i386 !m68k !powerpc !ppc64 !riscv64 !sh4 !x32 )"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > __gnu_cxx::__to_xstring<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>(int (*)(char*, unsigned int, char const*, std::__va_list), unsigned int, char const*, ...)@Base" 0.12.0
(optional|c++|arch= !amd64 !arm64 !armel !armhf !i386 !mips64el !ppc64el !s390x !alpha !hurd-i386 !ia64 !kfreebsd-amd64 !kfreebsd-i386 !powerpc !ppc64 !riscv64 !sh4 !x32 )"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > __gnu_cxx::__to_xstring<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>(int (*)(char*, unsigned int, char const*, void*), unsigned int, char const*, ...)@Base" 0.12.0
(optional|c++|arch= !amd64 !arm64 !armel !armhf !i386 !mips !mips64el !mipsel !s390x !alpha !hppa !ia64 !kfreebsd-amd64 !kfreebsd-i386 !m68k !powerpc !riscv64 !sh4 !x32 )"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > __gnu_cxx::__to_xstring<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>(int (*)(char*, unsigned long, char const*, char*), unsigned long, char const*, ...)@Base" 0.12.0
(optional|c++|arch= !amd64 !arm64 !armel !armhf !mips !mipsel !mips64el !ppc64el !s390x !alpha !ia64 !kfreebsd-amd64 !hppa !m68k !powerpc !ppc64 !riscv64 !sh4 !sparc64 !x32 )"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > __gnu_cxx::__to_xstring<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>(int (*)(char*, unsigned int, char const*, char*), unsigned int, char const*, ...)@Base" 0.12.0
(optional|c++|arch= !amd64 !arm64 !armel !armhf !i386 !mips !mips64el !mipsel !ppc64el !s390x !alpha !hppa !ia64 !kfreebsd-amd64 !kfreebsd-i386 !m68k !ppc64 !riscv64 !sh4 )"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > __gnu_cxx::__to_xstring<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>(int (*)(char*, unsigned int, char const*, __va_list_tag*), unsigned int, char const*, ...)@Base" 0.12.0
(optional|c++|arch= !amd64 !arm64 !armel !armhf !i386 !mips !mips64el !mipsel !ppc64el !s390x !alpha !hppa !ia64 !kfreebsd-amd64 !kfreebsd-i386 !m68k !powerpc !ppc64 !riscv64 !x32)"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > __gnu_cxx::__to_xstring<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>(int (*)(char*, unsigned int, char const*, __va_list_tag), unsigned int, char const*, ...)@Base" 0.12.0
(optional|c++|arch= !amd64 !arm64 !armel !armhf !i386 !mips !mipsel !ppc64el !s390x !alpha !hppa !kfreebsd-amd64 !kfreebsd-i386 !m68k !powerpc !ppc64 !sh4 !x32 )"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > __gnu_cxx::__to_xstring<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>(int (*)(char*, unsigned long, char const*, void*), unsigned long, char const*, ...)@Base" 0.12.0
(optional|c++|arch= !amd64 !armel !armhf !i386 !mips !mipsel !mips64el !ppc64el !s390x !alpha !hppa !hurd-i386 !ia64 !kfreebsd-amd64 !kfreebsd-i386 !m68k !powerpc !ppc64 !riscv64 !sh4 !sparc64 !x32 )"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > __gnu_cxx::__to_xstring<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>(int (*)(char*, unsigned long, char const*, std::__va_list), unsigned long, char const*, ...)@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.13.0~
(c++)"std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.13.0~
(c++)"std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.13.0~
(c++)"std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.13.0~
(optional|c++)"std::_Sp_counted_ptr_inplace<Fm::PlacesProxyModel, std::allocator<Fm::PlacesProxyModel>, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.13.0~
(optional|c++)"std::_Sp_counted_ptr_inplace<Fm::PlacesProxyModel, std::allocator<Fm::PlacesProxyModel>, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.13.0~
(optional|c++)"std::_Sp_counted_ptr_inplace<Fm::PlacesProxyModel, std::allocator<Fm::PlacesProxyModel>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.13.0~
(optional|c++)"std::_Sp_counted_ptr_inplace<Fm::PlacesProxyModel, std::allocator<Fm::PlacesProxyModel>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.13.0~
(optional|c++)"std::_Sp_counted_ptr_inplace<Fm::TemplateItem, std::allocator<Fm::TemplateItem>, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.13.0~
(optional|c++)"std::_Sp_counted_ptr_inplace<Fm::TemplateItem, std::allocator<Fm::TemplateItem>, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.13.0~
(optional|c++)"std::_Sp_counted_ptr_inplace<Fm::TemplateItem, std::allocator<Fm::TemplateItem>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.13.0~
(optional|c++)"std::_Sp_counted_ptr_inplace<Fm::TemplateItem, std::allocator<Fm::TemplateItem>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.13.0~
(optional|c++)"std::_Sp_counted_ptr_inplace<Fm::Templates, std::allocator<Fm::Templates>, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.13.0~
(optional|c++)"std::_Sp_counted_ptr_inplace<Fm::Templates, std::allocator<Fm::Templates>, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.13.0~
(optional|c++)"std::_Sp_counted_ptr_inplace<Fm::Templates, std::allocator<Fm::Templates>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.13.0~
(optional|c++)"std::_Sp_counted_ptr_inplace<Fm::Templates, std::allocator<Fm::Templates>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.13.0~
(c++)"std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)2>::_M_destroy()@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)2>::_M_dispose()@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)2>::_M_get_deleter(std::type_info const&)@Base" 0.12.0
(c++)"std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)2>::~_Sp_counted_ptr_inplace()@Base" 0.12.0
(c++)"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > __gnu_cxx::__to_xstring<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>(int (*)(char*, unsigned long, char const*, __va_list_tag*), unsigned long, char const*, ...)@Base" 0.12.0
(c++)"std::__detail::_Map_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::shared_ptr<Fm::FileInfo const> > >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const>, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true>, true>::operator[](std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.12.0
(c++)"std::__detail::_Map_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, Fm::FileInfoList>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, Fm::FileInfoList> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true>, true>::operator[](std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&)@Base" 0.13.0~
(c++)"std::pair<std::_Rb_tree_iterator<unsigned int>, bool> std::_Rb_tree<unsigned int, unsigned int, std::_Identity<unsigned int>, std::less<unsigned int>, std::allocator<unsigned int> >::_M_insert_unique<unsigned int>(unsigned int&&)@Base" 0.12.0
(optional=gcc8|c++)"std::pair<std::__detail::_Node_iterator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, char const*>, false, true>, bool> std::_Hashtable<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, char const*>, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, char const*> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_emplace<std::pair<char const*, char const*> >(std::integral_constant<bool, true>, std::pair<char const*, char const*>&&)@Base" 0.13.0~
(c++)"std::vector<Fm::BrowseHistoryItem, std::allocator<Fm::BrowseHistoryItem> >::_M_erase(__gnu_cxx::__normal_iterator<Fm::BrowseHistoryItem*, std::vector<Fm::BrowseHistoryItem, std::allocator<Fm::BrowseHistoryItem> > >)@Base" 0.12.0
(c++)"std::vector<Fm::DirTreeModelItem*, std::allocator<Fm::DirTreeModelItem*> >::_M_erase(__gnu_cxx::__normal_iterator<Fm::DirTreeModelItem**, std::vector<Fm::DirTreeModelItem*, std::allocator<Fm::DirTreeModelItem*> > >)@Base" 0.12.0
(c++)"std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >::_M_erase(__gnu_cxx::__normal_iterator<Fm::FilePath*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > >, __gnu_cxx::__normal_iterator<Fm::FilePath*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > >)@Base" 0.12.0
(c++|arch= !i386 !mips !mipsel !s390x !alpha !hurd-i386 !kfreebsd-i386 !powerpc !ppc64 )"std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >::insert(__gnu_cxx::__normal_iterator<Fm::FilePath const*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > >, Fm::FilePath const&)@Base" 0.12.0
(optional=gcc8|c++)"std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >::reserve(unsigned long)@Base" 0.13.0~
(c++)"std::vector<std::shared_ptr<Fm::BookmarkItem const>, std::allocator<std::shared_ptr<Fm::BookmarkItem const> > >::_M_erase(__gnu_cxx::__normal_iterator<std::shared_ptr<Fm::BookmarkItem const>*, std::vector<std::shared_ptr<Fm::BookmarkItem const>, std::allocator<std::shared_ptr<Fm::BookmarkItem const> > > >)@Base" 0.12.0
(c++)"std::vector<std::shared_ptr<Fm::BookmarkItem const>, std::allocator<std::shared_ptr<Fm::BookmarkItem const> > >::_M_insert_rval(__gnu_cxx::__normal_iterator<std::shared_ptr<Fm::BookmarkItem const> const*, std::vector<std::shared_ptr<Fm::BookmarkItem const>, std::allocator<std::shared_ptr<Fm::BookmarkItem const> > > >, std::shared_ptr<Fm::BookmarkItem const>&&)@Base" 0.12.0
(c++)"std::vector<std::shared_ptr<Fm::FileInfo const>, std::allocator<std::shared_ptr<Fm::FileInfo const> > >::operator=(std::vector<std::shared_ptr<Fm::FileInfo const>, std::allocator<std::shared_ptr<Fm::FileInfo const> > > const&)@Base" 0.12.0
(c++|arch= !armel !armhf !i386 !mips !mipsel !hppa !hurd-i386 !kfreebsd-i386 !m68k !powerpc !sh4 !x32 )"std::vector<std::shared_ptr<Fm::FileInfo const>, std::allocator<std::shared_ptr<Fm::FileInfo const> > >::reserve(unsigned long)@Base" 0.12.0
(optional|c++|arch= !amd64 !arm64 !mips64el !ppc64el !s390x !ia64 !kfreebsd-amd64 !alpha !ppc64 !riscv64 !sparc64 )"std::vector<std::shared_ptr<Fm::FileInfo const>, std::allocator<std::shared_ptr<Fm::FileInfo const> > >::reserve(unsigned int)@Base" 0.12.0
(c++)"std::vector<std::shared_ptr<Fm::TemplateItem>, std::allocator<std::shared_ptr<Fm::TemplateItem> > >::_M_erase(__gnu_cxx::__normal_iterator<std::shared_ptr<Fm::TemplateItem>*, std::vector<std::shared_ptr<Fm::TemplateItem>, std::allocator<std::shared_ptr<Fm::TemplateItem> > > >, __gnu_cxx::__normal_iterator<std::shared_ptr<Fm::TemplateItem>*, std::vector<std::shared_ptr<Fm::TemplateItem>, std::allocator<std::shared_ptr<Fm::TemplateItem> > > >)@Base" 0.13.0~
(c++)"std::vector<std::shared_ptr<Fm::Thumbnailer>, std::allocator<std::shared_ptr<Fm::Thumbnailer> > >::~vector()@Base" 0.12.0
(c++)"std::vector<std::unique_ptr<Fm::Archiver, std::default_delete<Fm::Archiver> >, std::allocator<std::unique_ptr<Fm::Archiver, std::default_delete<Fm::Archiver> > > >::~vector()@Base" 0.13.0~
(c++)"typeinfo for Fm::AppChooserComboBox@Base" 0.10.0
(c++)"typeinfo for Fm::AppChooserDialog@Base" 0.10.0
(c++)"typeinfo for Fm::AppMenuView@Base" 0.10.0
(c++)"typeinfo for Fm::BasicFileLauncher@Base" 0.13.0~
(c++)"typeinfo for Fm::BookmarkAction@Base" 0.10.0
(c++)"typeinfo for Fm::Bookmarks@Base" 0.12.0
(c++)"typeinfo for Fm::BrowseHistory@Base" 0.10.0
(c++)"typeinfo for Fm::CachedFolderModel@Base" 0.10.0
(c++)"typeinfo for Fm::ColorButton@Base" 0.10.0
(c++)"typeinfo for Fm::CopyJob@Base" 0.12.0
(c++)"typeinfo for Fm::CreateNewMenu@Base" 0.10.0
(c++)"typeinfo for Fm::DeleteJob@Base" 0.12.0
(c++)"typeinfo for Fm::DirListJob@Base" 0.12.0
@ -1167,6 +1238,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"typeinfo for Fm::FilePropsDialog@Base" 0.10.0
(c++)"typeinfo for Fm::FileSearchDialog@Base" 0.10.0
(c++)"typeinfo for Fm::FileSystemInfoJob@Base" 0.12.0
(c++)"typeinfo for Fm::FileTransferJob@Base" 0.13.0~
(c++)"typeinfo for Fm::Folder@Base" 0.12.0
(c++)"typeinfo for Fm::FolderItemDelegate@Base" 0.10.0
(c++)"typeinfo for Fm::FolderMenu@Base" 0.10.0
@ -1174,7 +1246,6 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"typeinfo for Fm::FolderModelItem@Base" 0.10.0
(c++)"typeinfo for Fm::FolderView@Base" 0.10.0
(c++)"typeinfo for Fm::FontButton@Base" 0.10.0
(c++)"typeinfo for Fm::IconTheme@Base" 0.10.0
(c++)"typeinfo for Fm::Job@Base" 0.12.0
(c++)"typeinfo for Fm::MountOperation@Base" 0.10.0
(c++)"typeinfo for Fm::PathBar@Base" 0.11.2
@ -1185,11 +1256,13 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"typeinfo for Fm::PlacesModelItem@Base" 0.10.0
(c++)"typeinfo for Fm::PlacesModelMountItem@Base" 0.10.0
(c++)"typeinfo for Fm::PlacesModelVolumeItem@Base" 0.10.0
(c++)"typeinfo for Fm::PlacesProxyModel@Base" 0.13.0~
(c++)"typeinfo for Fm::PlacesView@Base" 0.10.0
(c++)"typeinfo for Fm::ProxyFolderModel@Base" 0.10.0
(c++)"typeinfo for Fm::ProxyFolderModelFilter@Base" 0.12.0
(c++)"typeinfo for Fm::RenameDialog@Base" 0.10.0
(c++)"typeinfo for Fm::SidePane@Base" 0.10.0
(c++)"typeinfo for Fm::Templates@Base" 0.13.0~
(c++)"typeinfo for Fm::ThumbnailJob@Base" 0.12.0
(c++)"typeinfo for Fm::TotalSizeJob@Base" 0.12.0
(c++)"typeinfo for Fm::TrashJob@Base" 0.12.0
@ -1219,28 +1292,24 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++|arch= armel riscv64 )"typeinfo for std::_Sp_counted_ptr_inplace<Fm::IconInfo, std::allocator<Fm::IconInfo>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"typeinfo for std::_Sp_counted_ptr_inplace<Fm::MimeType, std::allocator<Fm::MimeType>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"typeinfo for std::_Sp_counted_ptr_inplace<Fm::MimeType, std::allocator<Fm::MimeType>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"typeinfo for std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"typeinfo for std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"typeinfo for std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"typeinfo for std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"typeinfo for std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"typeinfo for std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"typeinfo for std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"typeinfo for std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"typeinfo for std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"typeinfo for std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(optional|c++|arch=armel riscv64 )"typeinfo for __gnu_cxx::__mutex@Base" 0.12.0
(optional|c++|arch=armel riscv64 )"typeinfo name for __gnu_cxx::__mutex@Base" 0.12.0
(c++)"typeinfo for std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++)"typeinfo for std::_Sp_counted_ptr_inplace<Fm::PlacesProxyModel, std::allocator<Fm::PlacesProxyModel>, (__gnu_cxx::_Lock_policy)2>@Base" 0.13.0~
(c++)"typeinfo for std::_Sp_counted_ptr_inplace<Fm::TemplateItem, std::allocator<Fm::TemplateItem>, (__gnu_cxx::_Lock_policy)2>@Base" 0.13.0~
(c++)"typeinfo for std::_Sp_counted_ptr_inplace<Fm::Templates, std::allocator<Fm::Templates>, (__gnu_cxx::_Lock_policy)2>@Base" 0.13.0~
(c++)"typeinfo for std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++)"typeinfo for std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++)"typeinfo for std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++)"typeinfo for std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++)"typeinfo for std::_Sp_make_shared_tag@Base" 0.12.0
(c++)"typeinfo name for Fm::AppChooserComboBox@Base" 0.10.0
(c++)"typeinfo name for Fm::AppChooserDialog@Base" 0.10.0
(c++)"typeinfo name for Fm::AppMenuView@Base" 0.10.0
(c++)"typeinfo name for Fm::BasicFileLauncher@Base" 0.13.0~
(c++)"typeinfo name for Fm::BookmarkAction@Base" 0.10.0
(c++)"typeinfo name for Fm::Bookmarks@Base" 0.12.0
(c++)"typeinfo name for Fm::BrowseHistory@Base" 0.10.0
(c++)"typeinfo name for Fm::CachedFolderModel@Base" 0.10.0
(c++)"typeinfo name for Fm::ColorButton@Base" 0.10.0
(c++)"typeinfo name for Fm::CopyJob@Base" 0.12.0
(c++)"typeinfo name for Fm::CreateNewMenu@Base" 0.10.0
(c++)"typeinfo name for Fm::DeleteJob@Base" 0.12.0
(c++)"typeinfo name for Fm::DirListJob@Base" 0.12.0
@ -1262,6 +1331,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"typeinfo name for Fm::FilePropsDialog@Base" 0.10.0
(c++)"typeinfo name for Fm::FileSearchDialog@Base" 0.10.0
(c++)"typeinfo name for Fm::FileSystemInfoJob@Base" 0.12.0
(c++)"typeinfo name for Fm::FileTransferJob@Base" 0.13.0~
(c++)"typeinfo name for Fm::Folder@Base" 0.12.0
(c++)"typeinfo name for Fm::FolderItemDelegate@Base" 0.10.0
(c++)"typeinfo name for Fm::FolderMenu@Base" 0.10.0
@ -1269,7 +1339,6 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"typeinfo name for Fm::FolderModelItem@Base" 0.10.0
(c++)"typeinfo name for Fm::FolderView@Base" 0.10.0
(c++)"typeinfo name for Fm::FontButton@Base" 0.10.0
(c++)"typeinfo name for Fm::IconTheme@Base" 0.10.0
(c++)"typeinfo name for Fm::Job@Base" 0.12.0
(c++)"typeinfo name for Fm::MountOperation@Base" 0.10.0
(c++)"typeinfo name for Fm::PathBar@Base" 0.11.2
@ -1280,11 +1349,13 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"typeinfo name for Fm::PlacesModelItem@Base" 0.10.0
(c++)"typeinfo name for Fm::PlacesModelMountItem@Base" 0.10.0
(c++)"typeinfo name for Fm::PlacesModelVolumeItem@Base" 0.10.0
(c++)"typeinfo name for Fm::PlacesProxyModel@Base" 0.13.0~
(c++)"typeinfo name for Fm::PlacesView@Base" 0.10.0
(c++)"typeinfo name for Fm::ProxyFolderModel@Base" 0.10.0
(c++)"typeinfo name for Fm::ProxyFolderModelFilter@Base" 0.12.0
(c++)"typeinfo name for Fm::RenameDialog@Base" 0.10.0
(c++)"typeinfo name for Fm::SidePane@Base" 0.10.0
(c++)"typeinfo name for Fm::Templates@Base" 0.13.0~
(c++)"typeinfo name for Fm::ThumbnailJob@Base" 0.12.0
(c++)"typeinfo name for Fm::TotalSizeJob@Base" 0.12.0
(c++)"typeinfo name for Fm::TrashJob@Base" 0.12.0
@ -1315,9 +1386,10 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++|arch= !armel !riscv64 )"typeinfo name for std::_Sp_counted_ptr_inplace<Fm::MimeType, std::allocator<Fm::MimeType>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"typeinfo name for std::_Sp_counted_ptr_inplace<Fm::MimeType, std::allocator<Fm::MimeType>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"typeinfo name for std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"typeinfo name for std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"typeinfo name for std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"typeinfo name for std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++)"typeinfo name for std::_Sp_counted_ptr_inplace<Fm::PlacesProxyModel, std::allocator<Fm::PlacesProxyModel>, (__gnu_cxx::_Lock_policy)2>@Base" 0.13.0~
(c++)"typeinfo name for std::_Sp_counted_ptr_inplace<Fm::TemplateItem, std::allocator<Fm::TemplateItem>, (__gnu_cxx::_Lock_policy)2>@Base" 0.13.0~
(c++)"typeinfo name for std::_Sp_counted_ptr_inplace<Fm::Templates, std::allocator<Fm::Templates>, (__gnu_cxx::_Lock_policy)2>@Base" 0.13.0~
(c++)"typeinfo name for std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>@Base" 0.13.0~
(c++|arch= !armel !riscv64 )"typeinfo name for std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"typeinfo name for std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"typeinfo name for std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
@ -1325,11 +1397,15 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++|arch= !armel !riscv64 )"typeinfo name for std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"typeinfo name for std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++)"typeinfo name for std::_Sp_make_shared_tag@Base" 0.12.0
(optional=gcc8|c++)"void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char const*>(char const*, char const*, std::forward_iterator_tag)@Base" 0.13.0~
(optional=gcc8|c++)"void std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct<char*>(char*, char*, std::forward_iterator_tag)@Base" 0.13.0~
(c++)"void std::vector<Fm::BrowseHistoryItem, std::allocator<Fm::BrowseHistoryItem> >::_M_realloc_insert<Fm::BrowseHistoryItem>(__gnu_cxx::__normal_iterator<Fm::BrowseHistoryItem*, std::vector<Fm::BrowseHistoryItem, std::allocator<Fm::BrowseHistoryItem> > >, Fm::BrowseHistoryItem&&)@Base" 0.12.0
(optional|c++)"void std::vector<Fm::DirTreeModelItem*, std::allocator<Fm::DirTreeModelItem*> >::_M_realloc_insert<Fm::DirTreeModelItem*>(__gnu_cxx::__normal_iterator<Fm::DirTreeModelItem**, std::vector<Fm::DirTreeModelItem*, std::allocator<Fm::DirTreeModelItem*> > >, Fm::DirTreeModelItem*&&)@Base" 0.12.0
(optional=gcc8|c++)"void std::vector<Fm::DirTreeModelItem*, std::allocator<Fm::DirTreeModelItem*> >::emplace_back<Fm::DirTreeModelItem*>(Fm::DirTreeModelItem*&&)@Base" 0.13.0~
(c++)"void std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >::_M_range_insert<__gnu_cxx::__normal_iterator<Fm::FilePath const*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > > >(__gnu_cxx::__normal_iterator<Fm::FilePath*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > >, __gnu_cxx::__normal_iterator<Fm::FilePath const*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > >, __gnu_cxx::__normal_iterator<Fm::FilePath const*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > >, std::forward_iterator_tag)@Base" 0.12.0
(c++)"void std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >::_M_realloc_insert<Fm::FilePath const&>(__gnu_cxx::__normal_iterator<Fm::FilePath*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > >, Fm::FilePath const&)@Base" 0.12.0
(c++)"void std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >::_M_realloc_insert<Fm::FilePath&>(__gnu_cxx::__normal_iterator<Fm::FilePath*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > >, Fm::FilePath&)@Base" 0.13.0~
(c++)"void std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >::_M_realloc_insert<Fm::FilePath>(__gnu_cxx::__normal_iterator<Fm::FilePath*, std::vector<Fm::FilePath, std::allocator<Fm::FilePath> > >, Fm::FilePath&&)@Base" 0.12.0
(c++)"void std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >::emplace_back<Fm::FilePath&>(Fm::FilePath&)@Base" 0.13.0~
(c++)"void std::vector<Fm::FilePath, std::allocator<Fm::FilePath> >::emplace_back<Fm::FilePath>(Fm::FilePath&&)@Base" 0.12.0
(c++)"void std::vector<Fm::Mount, std::allocator<Fm::Mount> >::_M_realloc_insert<Fm::Mount>(__gnu_cxx::__normal_iterator<Fm::Mount*, std::vector<Fm::Mount, std::allocator<Fm::Mount> > >, Fm::Mount&&)@Base" 0.12.0
(c++)"void std::vector<Fm::Mount, std::allocator<Fm::Mount> >::emplace_back<Fm::Mount>(Fm::Mount&&)@Base" 0.12.0
@ -1343,16 +1419,20 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"void std::vector<std::shared_ptr<Fm::BookmarkItem const>, std::allocator<std::shared_ptr<Fm::BookmarkItem const> > >::_M_realloc_insert<std::shared_ptr<Fm::BookmarkItem const> >(__gnu_cxx::__normal_iterator<std::shared_ptr<Fm::BookmarkItem const>*, std::vector<std::shared_ptr<Fm::BookmarkItem const>, std::allocator<std::shared_ptr<Fm::BookmarkItem const> > > >, std::shared_ptr<Fm::BookmarkItem const>&&)@Base" 0.12.0
(c++)"void std::vector<std::shared_ptr<Fm::FileInfo const>, std::allocator<std::shared_ptr<Fm::FileInfo const> > >::_M_realloc_insert<std::shared_ptr<Fm::FileInfo const> >(__gnu_cxx::__normal_iterator<std::shared_ptr<Fm::FileInfo const>*, std::vector<std::shared_ptr<Fm::FileInfo const>, std::allocator<std::shared_ptr<Fm::FileInfo const> > > >, std::shared_ptr<Fm::FileInfo const>&&)@Base" 0.12.0
(c++)"void std::vector<std::shared_ptr<Fm::FileInfo const>, std::allocator<std::shared_ptr<Fm::FileInfo const> > >::_M_realloc_insert<std::shared_ptr<Fm::FileInfo const> const&>(__gnu_cxx::__normal_iterator<std::shared_ptr<Fm::FileInfo const>*, std::vector<std::shared_ptr<Fm::FileInfo const>, std::allocator<std::shared_ptr<Fm::FileInfo const> > > >, std::shared_ptr<Fm::FileInfo const> const&)@Base" 0.12.0
(c++)"void std::vector<std::shared_ptr<Fm::FileInfo const>, std::allocator<std::shared_ptr<Fm::FileInfo const> > >::emplace_back<std::shared_ptr<Fm::FileInfo const> const&>(std::shared_ptr<Fm::FileInfo const> const&)@Base" 0.13.0~
(c++)"void std::vector<std::shared_ptr<Fm::Folder>, std::allocator<std::shared_ptr<Fm::Folder> > >::_M_realloc_insert<std::shared_ptr<Fm::Folder> >(__gnu_cxx::__normal_iterator<std::shared_ptr<Fm::Folder>*, std::vector<std::shared_ptr<Fm::Folder>, std::allocator<std::shared_ptr<Fm::Folder> > > >, std::shared_ptr<Fm::Folder>&&)@Base" 0.13.0~
(c++)"void std::vector<std::shared_ptr<Fm::TemplateItem>, std::allocator<std::shared_ptr<Fm::TemplateItem> > >::_M_realloc_insert<std::shared_ptr<Fm::TemplateItem> >(__gnu_cxx::__normal_iterator<std::shared_ptr<Fm::TemplateItem>*, std::vector<std::shared_ptr<Fm::TemplateItem>, std::allocator<std::shared_ptr<Fm::TemplateItem> > > >, std::shared_ptr<Fm::TemplateItem>&&)@Base" 0.13.0~
(c++)"void std::vector<std::shared_ptr<Fm::Thumbnailer>, std::allocator<std::shared_ptr<Fm::Thumbnailer> > >::_M_realloc_insert<std::shared_ptr<Fm::Thumbnailer> >(__gnu_cxx::__normal_iterator<std::shared_ptr<Fm::Thumbnailer>*, std::vector<std::shared_ptr<Fm::Thumbnailer>, std::allocator<std::shared_ptr<Fm::Thumbnailer> > > >, std::shared_ptr<Fm::Thumbnailer>&&)@Base" 0.12.0
(c++)"void std::vector<std::unique_ptr<Fm::Archiver, std::default_delete<Fm::Archiver> >, std::allocator<std::unique_ptr<Fm::Archiver, std::default_delete<Fm::Archiver> > > >::_M_realloc_insert<std::unique_ptr<Fm::Archiver, std::default_delete<Fm::Archiver> > >(__gnu_cxx::__normal_iterator<std::unique_ptr<Fm::Archiver, std::default_delete<Fm::Archiver> >*, std::vector<std::unique_ptr<Fm::Archiver, std::default_delete<Fm::Archiver> >, std::allocator<std::unique_ptr<Fm::Archiver, std::default_delete<Fm::Archiver> > > > >, std::unique_ptr<Fm::Archiver, std::default_delete<Fm::Archiver> >&&)@Base" 0.13.0~
(c++)"vtable for Fm::AppChooserComboBox@Base" 0.10.0
(c++)"vtable for Fm::AppChooserDialog@Base" 0.10.0
(c++)"vtable for Fm::AppMenuView@Base" 0.10.0
(c++)"vtable for Fm::BasicFileLauncher@Base" 0.13.0~
(c++)"vtable for Fm::BookmarkAction@Base" 0.10.0
(c++)"vtable for Fm::Bookmarks@Base" 0.12.0
(c++)"vtable for Fm::BrowseHistory@Base" 0.10.0
(c++)"vtable for Fm::CachedFolderModel@Base" 0.10.0
(c++)"vtable for Fm::ColorButton@Base" 0.10.0
(c++)"vtable for Fm::CopyJob@Base" 0.12.0
(c++)"vtable for Fm::CreateNewMenu@Base" 0.10.0
(c++)"vtable for Fm::DeleteJob@Base" 0.12.0
(c++)"vtable for Fm::DirListJob@Base" 0.12.0
@ -1374,6 +1454,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"vtable for Fm::FilePropsDialog@Base" 0.10.0
(c++)"vtable for Fm::FileSearchDialog@Base" 0.10.0
(c++)"vtable for Fm::FileSystemInfoJob@Base" 0.12.0
(c++)"vtable for Fm::FileTransferJob@Base" 0.13.0~
(c++)"vtable for Fm::Folder@Base" 0.12.0
(c++)"vtable for Fm::FolderItemDelegate@Base" 0.10.0
(c++)"vtable for Fm::FolderMenu@Base" 0.10.0
@ -1381,7 +1462,6 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"vtable for Fm::FolderModelItem@Base" 0.10.0
(c++)"vtable for Fm::FolderView@Base" 0.10.0
(c++)"vtable for Fm::FontButton@Base" 0.10.0
(c++)"vtable for Fm::IconTheme@Base" 0.10.0
(c++)"vtable for Fm::Job@Base" 0.12.0
(c++)"vtable for Fm::MountOperation@Base" 0.10.0
(c++)"vtable for Fm::PathBar@Base" 0.11.2
@ -1392,10 +1472,12 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"vtable for Fm::PlacesModelItem@Base" 0.10.0
(c++)"vtable for Fm::PlacesModelMountItem@Base" 0.10.0
(c++)"vtable for Fm::PlacesModelVolumeItem@Base" 0.10.0
(c++)"vtable for Fm::PlacesProxyModel@Base" 0.13.0~
(c++)"vtable for Fm::PlacesView@Base" 0.10.0
(c++)"vtable for Fm::ProxyFolderModel@Base" 0.10.0
(c++)"vtable for Fm::RenameDialog@Base" 0.10.0
(c++)"vtable for Fm::SidePane@Base" 0.10.0
(c++)"vtable for Fm::Templates@Base" 0.13.0~
(c++)"vtable for Fm::ThumbnailJob@Base" 0.12.0
(c++)"vtable for Fm::TotalSizeJob@Base" 0.12.0
(c++)"vtable for Fm::TrashJob@Base" 0.12.0
@ -1403,7 +1485,7 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++)"vtable for Fm::UserInfoCache@Base" 0.12.0
(c++)"vtable for Fm::VolumeManager::GetGVolumeMonitorJob@Base" 0.12.0
(c++)"vtable for Fm::VolumeManager@Base" 0.12.0
(c++|arch= !armel !riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::BookmarkItem const, std::allocator<Fm::BookmarkItem>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(optional|c++|arch= !armel !riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::BookmarkItem const, std::allocator<Fm::BookmarkItem>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::BookmarkItem const, std::allocator<Fm::BookmarkItem>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::BookmarkItem, std::allocator<Fm::BookmarkItem>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::BookmarkItem, std::allocator<Fm::BookmarkItem>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
@ -1422,45 +1504,13 @@ libfm-qt.so.3 libfm-qt3 #MINVER#
(c++|arch= !armel !riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::MimeType, std::allocator<Fm::MimeType>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::MimeType, std::allocator<Fm::MimeType>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::PlacesModel, std::allocator<Fm::PlacesModel>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++)"vtable for std::_Sp_counted_ptr_inplace<Fm::PlacesProxyModel, std::allocator<Fm::PlacesProxyModel>, (__gnu_cxx::_Lock_policy)2>@Base" 0.13.0~
(c++)"vtable for std::_Sp_counted_ptr_inplace<Fm::TemplateItem, std::allocator<Fm::TemplateItem>, (__gnu_cxx::_Lock_policy)2>@Base" 0.13.0~
(c++)"vtable for std::_Sp_counted_ptr_inplace<Fm::Templates, std::allocator<Fm::Templates>, (__gnu_cxx::_Lock_policy)2>@Base" 0.13.0~
(c++)"vtable for std::_Sp_counted_ptr_inplace<Fm::Thumbnailer, std::allocator<Fm::Thumbnailer>, (__gnu_cxx::_Lock_policy)2>@Base" 0.13.0~
(c++|arch= !armel !riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::UserInfo, std::allocator<Fm::UserInfo>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"vtable for std::_Sp_counted_ptr_inplace<Fm::VolumeManager, std::allocator<Fm::VolumeManager>, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
(c++|arch= !armel !riscv64 )"vtable for std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)2>@Base" 0.12.0
(c++|arch= armel riscv64 )"vtable for std::_Sp_counted_ptr_inplace<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >, std::allocator<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >, (__gnu_cxx::_Lock_policy)1>@Base" 0.12.0
fm_search_add_dir@Base 0.10.0
fm_search_add_mime_type@Base 0.10.0
fm_search_dup_path@Base 0.10.0
fm_search_free@Base 0.10.0
fm_search_get_content_ci@Base 0.10.0
fm_search_get_content_pattern@Base 0.10.0
fm_search_get_content_regex@Base 0.10.0
fm_search_get_dirs@Base 0.10.0
fm_search_get_max_mtime@Base 0.10.0
fm_search_get_max_size@Base 0.10.0
fm_search_get_mime_types@Base 0.10.0
fm_search_get_min_mtime@Base 0.10.0
fm_search_get_min_size@Base 0.10.0
fm_search_get_name_ci@Base 0.10.0
fm_search_get_name_patterns@Base 0.10.0
fm_search_get_name_regex@Base 0.10.0
fm_search_get_recursive@Base 0.10.0
fm_search_get_show_hidden@Base 0.10.0
fm_search_new@Base 0.10.0
fm_search_remove_dir@Base 0.10.0
fm_search_remove_mime_type@Base 0.10.0
fm_search_set_content_ci@Base 0.10.0
fm_search_set_content_pattern@Base 0.10.0
fm_search_set_content_regex@Base 0.10.0
fm_search_set_max_mtime@Base 0.10.0
fm_search_set_max_size@Base 0.10.0
fm_search_set_min_mtime@Base 0.10.0
fm_search_set_min_size@Base 0.10.0
fm_search_set_name_ci@Base 0.10.0
fm_search_set_name_patterns@Base 0.10.0
fm_search_set_name_regex@Base 0.10.0
fm_search_set_recursive@Base 0.10.0
fm_search_set_show_hidden@Base 0.10.0

@ -9,7 +9,7 @@ set(libfm_core_SRCS
core/filemonitor.cpp
# i/o jobs
core/job.cpp
core/copyjob.cpp
core/filetransferjob.cpp
core/deletejob.cpp
core/dirlistjob.cpp
core/filechangeattrjob.cpp
@ -24,10 +24,13 @@ set(libfm_core_SRCS
core/thumbnailjob.cpp
# extra desktop services
core/bookmarks.cpp
core/basicfilelauncher.cpp
core/volumemanager.cpp
core/userinfocache.cpp
core/thumbnailer.cpp
core/terminal.cpp
core/archiver.cpp
core/templates.cpp
# custom actions
customactions/fileaction.cpp
customactions/fileactionprofile.cpp
@ -39,7 +42,6 @@ set(libfm_SRCS
libfmqt.cpp
bookmarkaction.cpp
sidepane.cpp
icontheme.cpp
filelauncher.cpp
foldermodel.cpp
foldermodelitem.cpp
@ -95,9 +97,6 @@ set(libfm_UIS
filedialog.ui
)
qt5_wrap_ui(libfm_UIS_H ${libfm_UIS})
set(LIBFM_QT_DATA_DIR "${CMAKE_INSTALL_FULL_DATADIR}/libfm-qt")
set(LIBFM_QT_INTREE_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/include")
@ -114,7 +113,7 @@ lxqt_translate_ts(QM_FILES
add_library(${LIBFM_QT_LIBRARY_NAME} SHARED
${libfm_SRCS}
${libfm_UIS_H}
${libfm_UIS}
${QM_FILES}
)
@ -154,6 +153,7 @@ target_include_directories(${LIBFM_QT_LIBRARY_NAME}
target_compile_definitions(${LIBFM_QT_LIBRARY_NAME}
PRIVATE "LIBFM_QT_DATA_DIR=\"${LIBFM_QT_DATA_DIR}\""
"QT_NO_FOREACH"
PUBLIC "QT_NO_KEYWORDS"
)
@ -216,7 +216,7 @@ export(TARGETS ${LIBFM_QT_LIBRARY_NAME}
set(REQUIRED_QT "Qt5Widgets >= ${REQUIRED_QT_VERSION} Qt5X11Extras >= ${REQUIRED_QT_VERSION}")
configure_file(libfm-qt.pc.in lib${LIBFM_QT_LIBRARY_NAME}.pc @ONLY)
# FreeBSD loves to install files to different locations
# http://www.freebsd.org/doc/handbook/dirstructure.html
# https://www.freebsd.org/doc/handbook/dirstructure.html
if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/lib${LIBFM_QT_LIBRARY_NAME}.pc"

@ -18,7 +18,6 @@
*/
#include "appchoosercombobox.h"
#include "icontheme.h"
#include "appchooserdialog.h"
#include "utilities.h"
#include "core/iconinfo.h"
@ -33,7 +32,7 @@ AppChooserComboBox::AppChooserComboBox(QWidget* parent):
// the new Qt5 signal/slot syntax cannot handle overloaded methods by default
// hence a type-casting is needed here. really ugly!
// reference: http://qt-project.org/forums/viewthread/21513
// reference: https://forum.qt.io/topic/20998/qt5-new-signals-slots-syntax-does-not-work-solved
connect((QComboBox*)this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &AppChooserComboBox::onCurrentIndexChanged);
}
@ -72,8 +71,10 @@ void AppChooserComboBox::setMimeType(std::shared_ptr<const Fm::MimeType> mimeTyp
// returns the currently selected app.
Fm::GAppInfoPtr AppChooserComboBox::selectedApp() const {
// the elements of appInfos_ and the combo indexes before "Customize"
// always have a one-to-one correspondence
int idx = currentIndex();
return idx >= 0 ? appInfos_[idx] : Fm::GAppInfoPtr{};
return idx >= 0 && !appInfos_.empty() ? appInfos_[idx] : Fm::GAppInfoPtr{};
}
bool AppChooserComboBox::isChanged() const {

@ -19,7 +19,6 @@
#include "appmenuview.h"
#include <QStandardItemModel>
#include "icontheme.h"
#include "appmenuview_p.h"
#include <gio/gdesktopappinfo.h>

@ -22,7 +22,6 @@
#include <QStandardItem>
#include <menu-cache/menu-cache.h>
#include "icontheme.h"
#include "core/iconinfo.h"
namespace Fm {

@ -1,143 +0,0 @@
/*
* Copyright (C) 2016 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef __LIBFM_QT_FM_ARCHIVER_H__
#define __LIBFM_QT_FM_ARCHIVER_H__
#include <libfm/fm.h>
#include <QObject>
#include <QtGlobal>
#include "libfmqtglobals.h"
namespace Fm {
class LIBFM_QT_API Archiver {
public:
// default constructor
Archiver() {
dataPtr_ = nullptr;
}
// move constructor
Archiver(Archiver&& other) noexcept {
dataPtr_ = reinterpret_cast<FmArchiver*>(other.takeDataPtr());
}
// destructor
~Archiver() {
if(dataPtr_ != nullptr) {
(dataPtr_);
}
}
// create a wrapper for the data pointer without increasing the reference count
static Archiver wrapPtr(FmArchiver* dataPtr) {
Archiver obj;
obj.dataPtr_ = reinterpret_cast<FmArchiver*>(dataPtr);
return obj;
}
// disown the managed data pointer
FmArchiver* takeDataPtr() {
FmArchiver* data = reinterpret_cast<FmArchiver*>(dataPtr_);
dataPtr_ = nullptr;
return data;
}
// get the raw pointer wrapped
FmArchiver* dataPtr() {
return reinterpret_cast<FmArchiver*>(dataPtr_);
}
// automatic type casting
operator FmArchiver*() {
return dataPtr();
}
// automatic type casting
operator void*() {
return dataPtr();
}
// move assignment
Archiver& operator=(Archiver&& other) noexcept {
dataPtr_ = reinterpret_cast<FmArchiver*>(other.takeDataPtr());
return *this;
}
bool isNull() {
return (dataPtr_ == nullptr);
}
// methods
void setDefault(void) {
fm_archiver_set_default(dataPtr());
}
static Archiver getDefault( ) {
return wrapPtr(fm_archiver_get_default());
}
bool extractArchivesTo(GAppLaunchContext* ctx, FmPathList* files, FmPath* dest_dir) {
return fm_archiver_extract_archives_to(dataPtr(), ctx, files, dest_dir);
}
bool extractArchives(GAppLaunchContext* ctx, FmPathList* files) {
return fm_archiver_extract_archives(dataPtr(), ctx, files);
}
bool createArchive(GAppLaunchContext* ctx, FmPathList* files) {
return fm_archiver_create_archive(dataPtr(), ctx, files);
}
bool isMimeTypeSupported(const char* type) {
return fm_archiver_is_mime_type_supported(dataPtr(), type);
}
// the wrapped object cannot be copied.
private:
Archiver(const Archiver& other) = delete;
Archiver& operator=(const Archiver& other) = delete;
private:
FmArchiver* dataPtr_; // data pointer for the underlying C struct
};
}
#endif // __LIBFM_QT_FM_ARCHIVER_H__

@ -59,7 +59,7 @@ void CachedFolderModel::unref() {
--refCount;
if(refCount <= 0) {
folder()->setProperty(cacheKey, QVariant());
deleteLater();
delete(this);
}
}

@ -0,0 +1,174 @@
#include "libfmqtglobals.h"
#include "archiver.h"
#include <string.h>
#include <glib.h>
#include <gio/gdesktopappinfo.h>
#include <string>
namespace Fm {
Archiver* Archiver::defaultArchiver_ = nullptr; // static
std::vector<std::unique_ptr<Archiver>> Archiver::allArchivers_; // static
Archiver::Archiver() {
}
bool Archiver::isMimeTypeSupported(const char* type) {
char** p;
if(G_UNLIKELY(!type)) {
return false;
}
for(p = mimeTypes_.get(); *p; ++p) {
if(strcmp(*p, type) == 0) {
return true;
}
}
return false;
}
bool Archiver::launchProgram(GAppLaunchContext* ctx, const char* cmd, const FilePathList& files, const FilePath& dir) {
char* _cmd = NULL;
const char* dir_place_holder;
GKeyFile* dummy;
if(dir.isValid() && (dir_place_holder = strstr(cmd, "%d"))) {
CStrPtr dir_str;
int len;
if(strstr(cmd, "%U") || strstr(cmd, "%u")) { /* supports URI */
dir_str = dir.uri();
}
else {
dir_str = dir.localPath();
}
// FIXME: remove libfm dependency here
/* replace all % with %% so encoded URI can be handled correctly when parsing Exec key. */
std::string percentEscapedDir;
for(auto p = dir_str.get(); *p; ++p) {
percentEscapedDir += *p;
if(*p == '%') {
percentEscapedDir += '%';
}
}
/* quote the path or URI */
dir_str = CStrPtr{g_shell_quote(percentEscapedDir.c_str())};
len = strlen(cmd) - 2 + strlen(dir_str.get()) + 1;
_cmd = (char*)g_malloc(len);
len = (dir_place_holder - cmd);
strncpy(_cmd, cmd, len);
strcpy(_cmd + len, dir_str.get());
strcat(_cmd, dir_place_holder + 2);
cmd = _cmd;
}
/* create a fake key file to cheat GDesktopAppInfo */
dummy = g_key_file_new();
g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Type", "Application");
g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Name", program_.get());
/* replace all % with %% so encoded URI can be handled correctly when parsing Exec key. */
g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Exec", cmd);
GAppInfoPtr app{reinterpret_cast<GAppInfo*>(g_desktop_app_info_new_from_keyfile(dummy)), false};
g_key_file_free(dummy);
g_debug("cmd = %s", cmd);
if(app) {
GList* uris = NULL;
for(auto& file: files) {
uris = g_list_prepend(uris, g_strdup(file.uri().get()));
}
g_app_info_launch_uris(app.get(), uris, ctx, NULL);
g_list_foreach(uris, (GFunc)g_free, NULL);
g_list_free(uris);
}
g_free(_cmd);
return true;
}
bool Archiver::createArchive(GAppLaunchContext* ctx, const FilePathList& files) {
if(createCmd_ && !files.empty()) {
launchProgram(ctx, createCmd_.get(), files, FilePath{});
}
return false;
}
bool Archiver::extractArchives(GAppLaunchContext* ctx, const FilePathList& files) {
if(extractCmd_ && !files.empty()) {
launchProgram(ctx, extractCmd_.get(), files, FilePath{});
}
return false;
}
bool Archiver::extractArchivesTo(GAppLaunchContext* ctx, const FilePathList& files, const FilePath& dest_dir) {
if(extractToCmd_ && !files.empty()) {
launchProgram(ctx, extractToCmd_.get(), files, dest_dir);
}
return false;
}
// static
Archiver* Archiver::defaultArchiver() {
allArchivers(); // to have a preliminary default archiver
return defaultArchiver_;
}
void Archiver::setDefaultArchiverByName(const char *name) {
if(name) {
auto& all = allArchivers();
for(auto& archiver: all) {
if(archiver->program_ && strcmp(archiver->program_.get(), name) == 0) {
defaultArchiver_ = archiver.get();
break;
}
}
}
}
// static
void Archiver::setDefaultArchiver(Archiver* archiver) {
if(archiver) {
defaultArchiver_ = archiver;
}
}
// static
const std::vector<std::unique_ptr<Archiver> >& Archiver::allArchivers() {
// load all archivers on demand
if(allArchivers_.empty()) {
GKeyFile* kf = g_key_file_new();
if(g_key_file_load_from_file(kf, LIBFM_QT_DATA_DIR "/archivers.list", G_KEY_FILE_NONE, NULL)) {
gsize n_archivers;
CStrArrayPtr programs{g_key_file_get_groups(kf, &n_archivers)};
if(programs) {
gsize i;
for(i = 0; i < n_archivers; ++i) {
auto program = programs[i];
std::unique_ptr<Archiver> archiver{new Archiver{}};
archiver->createCmd_ = CStrPtr{g_key_file_get_string(kf, program, "create", NULL)};
archiver->extractCmd_ = CStrPtr{g_key_file_get_string(kf, program, "extract", NULL)};
archiver->extractToCmd_ = CStrPtr{g_key_file_get_string(kf, program, "extract_to", NULL)};
archiver->mimeTypes_ = CStrArrayPtr{g_key_file_get_string_list(kf, program, "mime_types", NULL, NULL)};
archiver->program_ = CStrPtr{g_strdup(program)};
// if default archiver is not set, find the first program existing in the current system.
if(!defaultArchiver_) {
CStrPtr fullPath{g_find_program_in_path(program)};
if(fullPath) {
defaultArchiver_ = archiver.get();
}
}
allArchivers_.emplace_back(std::move(archiver));
}
}
}
g_key_file_free(kf);
}
return allArchivers_;
}
} // namespace Fm

@ -0,0 +1,69 @@
#ifndef ARCHIVER_H
#define ARCHIVER_H
#include "../libfmqtglobals.h"
#include "filepath.h"
#include "gioptrs.h"
#include <vector>
#include <memory>
namespace Fm {
class LIBFM_QT_API Archiver {
public:
Archiver();
bool isMimeTypeSupported(const char* type);
bool canCreateArchive() const {
return createCmd_ != nullptr;
}
bool createArchive(GAppLaunchContext* ctx, const FilePathList& files);
bool canExtractArchives() const {
return extractCmd_ != nullptr;
}
bool extractArchives(GAppLaunchContext* ctx, const FilePathList& files);
bool canExtractArchivesTo() const {
return extractToCmd_ != nullptr;
}
bool extractArchivesTo(GAppLaunchContext* ctx, const FilePathList& files, const FilePath& dest_dir);
/* get default GUI archivers used by libfm */
static Archiver* defaultArchiver();
/* set default GUI archivers used by libfm */
static void setDefaultArchiverByName(const char* name);
/* set default GUI archivers used by libfm */
static void setDefaultArchiver(Archiver* archiver);
/* get a list of FmArchiver* of all GUI archivers known to libfm */
static const std::vector<std::unique_ptr<Archiver>>& allArchivers();
const char* program() const {
return program_.get();
}
private:
bool launchProgram(GAppLaunchContext* ctx, const char* cmd, const FilePathList& files, const FilePath &dir);
private:
CStrPtr program_;
CStrPtr createCmd_;
CStrPtr extractCmd_;
CStrPtr extractToCmd_;
CStrArrayPtr mimeTypes_;
static Archiver* defaultArchiver_;
static std::vector<std::unique_ptr<Archiver>> allArchivers_;
};
} // namespace Fm
#endif // ARCHIVER_H

@ -0,0 +1,345 @@
#include "basicfilelauncher.h"
#include "fileinfojob.h"
#include "mountoperation.h"
#include <gio/gdesktopappinfo.h>
#include <glib/gi18n.h>
#include <unordered_map>
#include <string>
#include <QObject>
#include <QEventLoop>
#include <QDebug>
namespace Fm {
BasicFileLauncher::BasicFileLauncher():
quickExec_{false} {
}
BasicFileLauncher::~BasicFileLauncher() {
}
bool BasicFileLauncher::launchFiles(const FileInfoList& fileInfos, GAppLaunchContext* ctx) {
std::unordered_map<std::string, FileInfoList> mimeTypeToFiles;
FileInfoList folderInfos;
FilePathList pathsToLaunch;
// classify files according to different mimetypes
for(auto& fileInfo : fileInfos) {
// qDebug("path: %s, target: %s", fileInfo->path().toString().get(), fileInfo->target().c_str());
if(fileInfo->isDir()) {
folderInfos.emplace_back(fileInfo);
}
else if(fileInfo->isMountable()) {
if(fileInfo->target().empty()) {
// the mountable is not yet mounted so we have no target URI.
GErrorPtr err{G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED,
QObject::tr("The path is not mounted.")};
if(!showError(ctx, err, fileInfo->path(), fileInfo)) {
// the user fail to handle the error, skip this file.
continue;
}
// we do not have the target path in the FileInfo object.
// try to launch our path again to query the new file info later so we can get the mounted target URI.
pathsToLaunch.emplace_back(fileInfo->path());
}
else {
// we have the target path, launch it later
pathsToLaunch.emplace_back(FilePath::fromPathStr(fileInfo->target().c_str()));
}
}
else if(fileInfo->isDesktopEntry()) {
// launch the desktop entry
launchDesktopEntry(fileInfo, FilePathList{}, ctx);
}
else if(fileInfo->isExecutableType()) {
// directly execute the file
launchExecutable(fileInfo, ctx);
}
else if(fileInfo->isShortcut()) {
// for shortcuts, launch their targets instead
auto path = handleShortcut(fileInfo, ctx);
if(path.isValid()) {
pathsToLaunch.emplace_back(path);
}
}
else {
auto& mimeType = fileInfo->mimeType();
mimeTypeToFiles[mimeType->name()].emplace_back(fileInfo);
}
}
// open folders
if(!folderInfos.empty()) {
GErrorPtr err;
openFolder(ctx, folderInfos, err);
}
// open files of different mime-types with their default app
for(auto& typeFiles : mimeTypeToFiles) {
auto& mimeType = typeFiles.first;
auto& files = typeFiles.second;
GErrorPtr err;
GAppInfoPtr app{g_app_info_get_default_for_type(mimeType.c_str(), false), false};
if(!app) {
app = chooseApp(files, mimeType.c_str(), err);
}
if(app) {
launchWithApp(app.get(), files.paths(), ctx);
}
}
if(!pathsToLaunch.empty()) {
launchPaths(pathsToLaunch, ctx);
}
return true;
}
bool BasicFileLauncher::launchPaths(FilePathList paths, GAppLaunchContext* ctx) {
// FIXME: blocking with an event loop is not a good design :-(
QEventLoop eventLoop;
auto job = new FileInfoJob{paths};
job->setAutoDelete(false); // do not automatically delete the job since we want its results later.
GObjectPtr<GAppLaunchContext> ctxPtr{ctx};
QObject::connect(job, &FileInfoJob::finished,
[&eventLoop]() {
// exit the event loop when the job is done
eventLoop.exit();
});
// run the job in another thread to not block the UI
job->runAsync();
// blocking until the job is done with a event loop
eventLoop.exec();
// launch the file info
launchFiles(job->files(), ctx);
delete job;
return false;
}
GAppInfoPtr BasicFileLauncher::chooseApp(const FileInfoList& /* fileInfos */, const char* /*mimeType*/, GErrorPtr& /* err */) {
return GAppInfoPtr{};
}
bool BasicFileLauncher::openFolder(GAppLaunchContext* ctx, const FileInfoList& folderInfos, GErrorPtr& err) {
auto app = chooseApp(folderInfos, "inode/directory", err);
if(app) {
launchWithApp(app.get(), folderInfos.paths(), ctx);
}
else {
showError(ctx, err);
}
return false;
}
BasicFileLauncher::ExecAction BasicFileLauncher::askExecFile(const FileInfoPtr & /* file */) {
return ExecAction::DIRECT_EXEC;
}
bool BasicFileLauncher::showError(GAppLaunchContext* /* ctx */, GErrorPtr& /* err */, const FilePath& /* path */, const FileInfoPtr& /* info */) {
return false;
}
int BasicFileLauncher::ask(const char* /* msg */, char* const* /* btn_labels */, int default_btn) {
return default_btn;
}
bool BasicFileLauncher::launchWithApp(GAppInfo* app, const FilePathList& paths, GAppLaunchContext* ctx) {
GList* uris = nullptr;
for(auto& path : paths) {
auto uri = path.uri();
uris = g_list_prepend(uris, uri.release());
}
GErrorPtr err;
bool ret = bool(g_app_info_launch_uris(app, uris, ctx, &err));
g_list_foreach(uris, reinterpret_cast<GFunc>(g_free), nullptr);
g_list_free(uris);
if(!ret) {
// FIXME: show error for all files
showError(ctx, err, paths[0]);
}
return ret;
}
bool BasicFileLauncher::launchDesktopEntry(const FileInfoPtr &fileInfo, const FilePathList &paths, GAppLaunchContext* ctx) {
/* treat desktop entries as executables */
auto target = fileInfo->target();
CStrPtr filename;
const char* desktopEntryName = nullptr;
FilePathList shortcutTargetPaths;
if(fileInfo->isExecutableType()) {
auto act = quickExec_ ? ExecAction::DIRECT_EXEC : askExecFile(fileInfo);
switch(act) {
case ExecAction::EXEC_IN_TERMINAL:
case ExecAction::DIRECT_EXEC: {
if(fileInfo->isShortcut()) {
auto path = handleShortcut(fileInfo, ctx);
if(path.isValid()) {
shortcutTargetPaths.emplace_back(path);
}
}
else {
if(target.empty()) {
filename = fileInfo->path().localPath();
}
desktopEntryName = !target.empty() ? target.c_str() : filename.get();
}
break;
}
case ExecAction::OPEN_WITH_DEFAULT_APP:
return launchWithDefaultApp(fileInfo, ctx);
case ExecAction::CANCEL:
return false;
default:
return false;
}
}
/* make exception for desktop entries under menu */
else if(fileInfo->isNative() /* an exception */ ||
fileInfo->path().hasUriScheme("menu")) {
if(target.empty()) {
filename = fileInfo->path().localPath();
}
desktopEntryName = !target.empty() ? target.c_str() : filename.get();
}
if(desktopEntryName) {
return launchDesktopEntry(desktopEntryName, paths, ctx);
}
if(!shortcutTargetPaths.empty()) {
launchPaths(shortcutTargetPaths, ctx);
}
return false;
}
bool BasicFileLauncher::launchDesktopEntry(const char *desktopEntryName, const FilePathList &paths, GAppLaunchContext *ctx) {
bool ret = false;
GAppInfo* app;
/* Let GDesktopAppInfo try first. */
if(g_path_is_absolute(desktopEntryName)) {
app = G_APP_INFO(g_desktop_app_info_new_from_filename(desktopEntryName));
}
else {
app = G_APP_INFO(g_desktop_app_info_new(desktopEntryName));
}
/* we handle Type=Link in FmFileInfo so if GIO failed then
it cannot be launched in fact */
if(app) {
return launchWithApp(app, paths, ctx);
}
else {
QString msg = QObject::tr("Invalid desktop entry file: '%1'").arg(desktopEntryName);
GErrorPtr err{G_IO_ERROR, G_IO_ERROR_FAILED, msg};
showError(ctx, err);
}
return ret;
}
FilePath BasicFileLauncher::handleShortcut(const FileInfoPtr& fileInfo, GAppLaunchContext* ctx) {
auto target = fileInfo->target();
auto scheme = CStrPtr{g_uri_parse_scheme(target.c_str())};
if(scheme) {
// collect the uri schemes we support
if(strcmp(scheme.get(), "file") == 0
|| strcmp(scheme.get(), "trash") == 0
|| strcmp(scheme.get(), "network") == 0
|| strcmp(scheme.get(), "computer") == 0) {
return FilePath::fromUri(fileInfo->target().c_str());
}
else {
// ask gio to launch the default handler for the uri scheme
GAppInfoPtr app{g_app_info_get_default_for_uri_scheme(scheme.get()), false};
FilePathList uris{FilePath::fromUri(fileInfo->target().c_str())};
launchWithApp(app.get(), uris, ctx);
}
}
else {
// see it as a local path
return FilePath::fromLocalPath(fileInfo->target().c_str());
}
return FilePath();
}
bool BasicFileLauncher::launchExecutable(const FileInfoPtr &fileInfo, GAppLaunchContext* ctx) {
/* if it's an executable file, directly execute it. */
auto filename = fileInfo->path().localPath();
/* FIXME: we need to use eaccess/euidaccess here. */
if(g_file_test(filename.get(), G_FILE_TEST_IS_EXECUTABLE)) {
auto act = quickExec_ ? ExecAction::DIRECT_EXEC : askExecFile(fileInfo);
int flags = G_APP_INFO_CREATE_NONE;
switch(act) {
case ExecAction::EXEC_IN_TERMINAL:
flags |= G_APP_INFO_CREATE_NEEDS_TERMINAL;
/* Falls through. */
case ExecAction::DIRECT_EXEC: {
/* filename may contain spaces. Fix #3143296 */
CStrPtr quoted{g_shell_quote(filename.get())};
// FIXME: remove libfm dependency
GAppInfoPtr app{fm_app_info_create_from_commandline(quoted.get(), nullptr, GAppInfoCreateFlags(flags), nullptr)};
if(app) {
CStrPtr run_path{g_path_get_dirname(filename.get())};
CStrPtr cwd;
/* bug #3589641: scripts are ran from $HOME.
since GIO launcher is kinda ugly - it has
no means to set running directory so we
do workaround - change directory to it */
if(run_path && strcmp(run_path.get(), ".")) {
cwd = CStrPtr{g_get_current_dir()};
if(chdir(run_path.get()) != 0) {
cwd.reset();
// show errors
QString msg = QObject::tr("Cannot set working directory to '%1': %2").arg(run_path.get()).arg(g_strerror(errno));
GErrorPtr err{G_IO_ERROR, g_io_error_from_errno(errno), msg};
showError(ctx, err);
}
}
// FIXME: remove libfm dependency
GErrorPtr err;
if(!fm_app_info_launch(app.get(), nullptr, ctx, &err)) {
showError(ctx, err);
}
if(cwd) { /* return back */
if(chdir(cwd.get()) != 0) {
g_warning("fm_launch_files(): chdir() failed");
}
}
return true;
}
break;
}
case ExecAction::OPEN_WITH_DEFAULT_APP:
return launchWithDefaultApp(fileInfo, ctx);
case ExecAction::CANCEL:
default:
break;
}
}
return false;
}
bool BasicFileLauncher::launchWithDefaultApp(const FileInfoPtr &fileInfo, GAppLaunchContext* ctx) {
FileInfoList files;
files.emplace_back(fileInfo);
GErrorPtr err;
GAppInfoPtr app{g_app_info_get_default_for_type(fileInfo->mimeType()->name(), false), false};
if(app) {
return launchWithApp(app.get(), files.paths(), ctx);
}
else {
showError(ctx, err, fileInfo->path());
}
return false;
}
} // namespace Fm

@ -0,0 +1,72 @@
#ifndef BASICFILELAUNCHER_H
#define BASICFILELAUNCHER_H
#include "../libfmqtglobals.h"
#include "fileinfo.h"
#include "filepath.h"
#include "mimetype.h"
#include <gio/gio.h>
namespace Fm {
class LIBFM_QT_API BasicFileLauncher {
public:
enum class ExecAction {
NONE,
DIRECT_EXEC,
EXEC_IN_TERMINAL,
OPEN_WITH_DEFAULT_APP,
CANCEL
};
explicit BasicFileLauncher();
virtual ~BasicFileLauncher();
bool launchFiles(const FileInfoList &fileInfos, GAppLaunchContext* ctx = nullptr);
bool launchPaths(FilePathList paths, GAppLaunchContext* ctx = nullptr);
bool launchDesktopEntry(const FileInfoPtr &fileInfo, const FilePathList& paths = FilePathList{}, GAppLaunchContext* ctx = nullptr);
bool launchDesktopEntry(const char* desktopEntryName, const FilePathList& paths = FilePathList{}, GAppLaunchContext* ctx = nullptr);
bool launchWithDefaultApp(const FileInfoPtr& fileInfo, GAppLaunchContext* ctx = nullptr);
bool launchWithApp(GAppInfo* app, const FilePathList& paths, GAppLaunchContext* ctx = nullptr);
bool launchExecutable(const FileInfoPtr &fileInfo, GAppLaunchContext* ctx = nullptr);
bool quickExec() const {
return quickExec_;
}
void setQuickExec(bool value) {
quickExec_ = value;
}
protected:
virtual GAppInfoPtr chooseApp(const FileInfoList& fileInfos, const char* mimeType, GErrorPtr& err);
virtual bool openFolder(GAppLaunchContext* ctx, const FileInfoList& folderInfos, GErrorPtr& err);
virtual bool showError(GAppLaunchContext* ctx, GErrorPtr& err, const FilePath& path = FilePath{}, const FileInfoPtr& info = FileInfoPtr{});
virtual ExecAction askExecFile(const FileInfoPtr& file);
virtual int ask(const char* msg, char* const* btn_labels, int default_btn);
private:
FilePath handleShortcut(const FileInfoPtr &fileInfo, GAppLaunchContext* ctx = nullptr);
private:
bool quickExec_; // Don't ask options on launch executable file
};
} // namespace Fm
#endif // BASICFILELAUNCHER_H

@ -2,6 +2,7 @@
#include "cstrptr.h"
#include <algorithm>
#include <QTimer>
#include <QStandardPaths>
namespace Fm {
@ -15,6 +16,60 @@ static inline CStrPtr get_new_bookmarks_file(void) {
return CStrPtr{g_build_filename(g_get_user_config_dir(), "gtk-3.0", "bookmarks", nullptr)};
}
BookmarkItem::BookmarkItem(const FilePath& path, const QString name):
path_{path},
name_{name} {
if(name_.isEmpty()) { // if the name is not specified, use basename of the path
name_ = path_.baseName().get();
}
// We cannot rely on FileInfos to set bookmark icons because there is no guarantee
// that FileInfos already exist, while their creation is costly. Therefore, we have
// to get folder icons directly, as is done at `FileInfo::setFromGFileInfo` and more.
auto local_path = path.localPath();
auto dot_dir = CStrPtr{g_build_filename(local_path.get(), ".directory", nullptr)};
if(g_file_test(dot_dir.get(), G_FILE_TEST_IS_REGULAR)) {
GKeyFile* kf = g_key_file_new();
if(g_key_file_load_from_file(kf, dot_dir.get(), G_KEY_FILE_NONE, nullptr)) {
CStrPtr icon_name{g_key_file_get_string(kf, "Desktop Entry", "Icon", nullptr)};
if(icon_name) {
icon_ = IconInfo::fromName(icon_name.get());
}
}
g_key_file_free(kf);
}
if(!icon_ || !icon_->isValid()) {
// first check some standard folders that are shared by Qt and GLib
if(path_ == FilePath::homeDir()) {
icon_ = IconInfo::fromName("user-home");
}
else if (path_.parent() == FilePath::homeDir()) {
QString folderPath = QString::fromUtf8(path_.toString().get());
if(folderPath == QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)) {
icon_ = IconInfo::fromName("user-desktop");
}
else if(folderPath == QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)) {
icon_ = IconInfo::fromName("folder-documents");
}
else if(folderPath == QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)) {
icon_ = IconInfo::fromName("folder-download");
}
else if(folderPath == QStandardPaths::writableLocation(QStandardPaths::MusicLocation)) {
icon_ = IconInfo::fromName("folder-music");
}
else if(folderPath == QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)) {
icon_ = IconInfo::fromName("folder-pictures");
}
else if(folderPath == QStandardPaths::writableLocation(QStandardPaths::MoviesLocation)) {
icon_ = IconInfo::fromName("folder-videos");
}
}
// fall back to the default folder icon
if(!icon_ || !icon_->isValid()) {
icon_ = IconInfo::fromName("folder");
}
}
}
Bookmarks::Bookmarks(QObject* parent):
QObject(parent),
idle_handler{false} {

@ -1,11 +1,10 @@
#ifndef FM2_BOOKMARKS_H
#define FM2_BOOKMARKS_H
#include "../libfmqtglobals.h"
#include <QObject>
#include "gobjectptr.h"
#include "fileinfo.h"
#include "filepath.h"
#include "iconinfo.h"
namespace Fm {
@ -13,11 +12,7 @@ class LIBFM_QT_API BookmarkItem {
public:
friend class Bookmarks;
explicit BookmarkItem(const FilePath& path, const QString name): path_{path}, name_{name} {
if(name_.isEmpty()) { // if the name is not specified, use basename of the path
name_ = path_.baseName().get();
}
}
explicit BookmarkItem(const FilePath& path, const QString name);
const QString& name() const {
return name_;
@ -27,15 +22,11 @@ public:
return path_;
}
const std::shared_ptr<const FmFileInfo>& info() const {
return info_;
const std::shared_ptr<const IconInfo>& icon() const {
return icon_;
}
private:
void setInfo(const std::shared_ptr<const FmFileInfo>& info) {
info_ = info;
}
void setName(const QString& name) {
name_ = name;
}
@ -43,7 +34,7 @@ private:
private:
FilePath path_;
QString name_;
std::shared_ptr<const FmFileInfo> info_;
std::shared_ptr<const IconInfo> icon_;
};

@ -1,53 +0,0 @@
#ifndef LIBFM_QT_COMPAT_P_H
#define LIBFM_QT_COMPAT_P_H
#include "../libfmqtglobals.h"
#include "core/filepath.h"
#include "core/fileinfo.h"
#include "core/gioptrs.h"
// deprecated
#include <libfm/fm.h>
#include "path.h"
// compatibility functions bridging the old libfm C APIs and new C++ APIs.
namespace Fm {
inline FM_QT_DEPRECATED Fm::Path _convertPath(const Fm::FilePath& path) {
return Fm::Path::newForGfile(path.gfile().get());
}
inline FM_QT_DEPRECATED Fm::PathList _convertPathList(const Fm::FilePathList& srcFiles) {
Fm::PathList ret;
for(auto& file: srcFiles) {
ret.pushTail(_convertPath(file));
}
return ret;
}
inline FM_QT_DEPRECATED FmFileInfo* _convertFileInfo(const std::shared_ptr<const Fm::FileInfo>& info) {
// conver to GFileInfo first
GFileInfoPtr ginfo{g_file_info_new(), false};
g_file_info_set_name(ginfo.get(), info->name().c_str());
g_file_info_set_display_name(ginfo.get(), info->displayName().toUtf8().constData());
g_file_info_set_content_type(ginfo.get(), info->mimeType()->name());
auto mode = info->mode();
g_file_info_set_attribute_uint32(ginfo.get(), G_FILE_ATTRIBUTE_UNIX_MODE, mode);
GFileType ftype = info->isDir() ? G_FILE_TYPE_DIRECTORY : G_FILE_TYPE_REGULAR; // FIXME: generate more accurate type
g_file_info_set_file_type(ginfo.get(), ftype);
g_file_info_set_size(ginfo.get(), info->size());
g_file_info_set_icon(ginfo.get(), info->icon()->gicon().get());
g_file_info_set_attribute_uint64(ginfo.get(), G_FILE_ATTRIBUTE_TIME_MODIFIED, info->mtime());
g_file_info_set_attribute_uint64(ginfo.get(), G_FILE_ATTRIBUTE_TIME_ACCESS, info->atime());
g_file_info_set_attribute_uint64(ginfo.get(), G_FILE_ATTRIBUTE_TIME_CHANGED, info->ctime());
auto gf = info->path().gfile();
return fm_file_info_new_from_g_file_data(gf.get(), ginfo.get(), nullptr);
}
}
#endif // LIBFM_QT_COMPAT_P_H

@ -1,453 +0,0 @@
#include "copyjob.h"
#include "totalsizejob.h"
#include "fileinfo_p.h"
namespace Fm {
CopyJob::CopyJob(const FilePathList& paths, const FilePath& destDirPath, Mode mode):
FileOperationJob{},
srcPaths_{paths},
destDirPath_{destDirPath},
mode_{mode},
skip_dir_content{false} {
}
CopyJob::CopyJob(const FilePathList &&paths, const FilePath &&destDirPath, Mode mode):
FileOperationJob{},
srcPaths_{paths},
destDirPath_{destDirPath},
mode_{mode},
skip_dir_content{false} {
}
void CopyJob::gfileProgressCallback(goffset current_num_bytes, goffset total_num_bytes, CopyJob* _this) {
_this->setCurrentFileProgress(total_num_bytes, current_num_bytes);
}
bool CopyJob::copyRegularFile(const FilePath& srcPath, GFileInfoPtr /*srcFile*/, const FilePath& destPath) {
int flags = G_FILE_COPY_ALL_METADATA | G_FILE_COPY_NOFOLLOW_SYMLINKS;
GErrorPtr err;
_retry_copy:
if(!g_file_copy(srcPath.gfile().get(), destPath.gfile().get(), GFileCopyFlags(flags), cancellable().get(),
GFileProgressCallback(gfileProgressCallback), this, &err)) {
flags &= ~G_FILE_COPY_OVERWRITE;
/* handle existing files or file name conflict */
if(err.domain() == G_IO_ERROR && (err.code() == G_IO_ERROR_EXISTS ||
err.code() == G_IO_ERROR_INVALID_FILENAME ||
err.code() == G_IO_ERROR_FILENAME_TOO_LONG)) {
#if 0
GFile* dest_cp = new_dest;
bool dest_exists = (err->code == G_IO_ERROR_EXISTS);
FmFileOpOption opt = 0;
g_error_free(err);
err = nullptr;
new_dest = nullptr;
opt = _fm_file_ops_job_ask_new_name(job, src, dest, &new_dest, dest_exists);
if(!new_dest) { /* restoring status quo */
new_dest = dest_cp;
}
else if(dest_cp) { /* we got new new_dest, forget old one */
g_object_unref(dest_cp);
}
switch(opt) {
case FM_FILE_OP_RENAME:
dest = new_dest;
goto _retry_copy;
break;
case FM_FILE_OP_OVERWRITE:
flags |= G_FILE_COPY_OVERWRITE;
goto _retry_copy;
break;
case FM_FILE_OP_CANCEL:
fm_job_cancel(fmjob);
break;
case FM_FILE_OP_SKIP:
ret = true;
delete_src = false; /* don't delete source file. */
break;
case FM_FILE_OP_SKIP_ERROR: ; /* FIXME */
}
#endif
}
else {
ErrorAction act = emitError( err, ErrorSeverity::MODERATE);
err.reset();
if(act == ErrorAction::RETRY) {
// FIXME: job->current_file_finished = 0;
goto _retry_copy;
}
# if 0
const bool is_no_space = (err.domain() == G_IO_ERROR &&
err.code() == G_IO_ERROR_NO_SPACE);
/* FIXME: ask to leave partial content? */
if(is_no_space) {
g_file_delete(dest, fm_job_get_cancellable(fmjob), nullptr);
}
ret = false;
delete_src = false;
#endif
}
err.reset();
}
else {
return true;
}
return false;
}
bool CopyJob::copySpecialFile(const FilePath& srcPath, GFileInfoPtr srcFile, const FilePath& destPath) {
bool ret = false;
GError* err = nullptr;
/* only handle FIFO for local files */
if(srcPath.isNative() && destPath.isNative()) {
auto src_path = srcPath.localPath();
struct stat src_st;
int r;
r = lstat(src_path.get(), &src_st);
if(r == 0) {
/* Handle FIFO on native file systems. */
if(S_ISFIFO(src_st.st_mode)) {
auto dest_path = destPath.localPath();
if(mkfifo(dest_path.get(), src_st.st_mode) == 0) {
ret = true;
}
}
/* FIXME: how about block device, char device, and socket? */
}
}
if(!ret) {
g_set_error(&err, G_IO_ERROR, G_IO_ERROR_FAILED,
("Cannot copy file '%s': not supported"),
g_file_info_get_display_name(srcFile.get()));
// emitError( err, ErrorSeverity::MODERATE);
g_clear_error(&err);
}
return ret;
}
bool CopyJob::copyDir(const FilePath& srcPath, GFileInfoPtr srcFile, const FilePath& destPath) {
bool ret = false;
if(makeDir(srcPath, srcFile, destPath)) {
GError* err = nullptr;
auto enu = GFileEnumeratorPtr{
g_file_enumerate_children(srcPath.gfile().get(),
gfile_info_query_attribs,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err),
false};
if(enu) {
int n_children = 0;
int n_copied = 0;
ret = true;
while(!isCancelled()) {
auto inf = GFileInfoPtr{g_file_enumerator_next_file(enu.get(), cancellable().get(), &err), false};
if(inf) {
++n_children;
/* don't overwrite dir content, only calculate progress. */
if(Q_UNLIKELY(skip_dir_content)) {
/* FIXME: this is incorrect as we don't do the calculation recursively. */
addFinishedAmount(g_file_info_get_size(inf.get()), 1);
}
else {
const char* name = g_file_info_get_name(inf.get());
FilePath childPath = srcPath.child(name);
bool child_ret = copyPath(childPath, inf, destPath, name);
if(child_ret) {
++n_copied;
}
else {
ret = false;
}
}
}
else {
if(err) {
// FIXME: emitError( err, ErrorSeverity::MODERATE);
g_error_free(err);
err = nullptr;
/* ErrorAction::RETRY is not supported here */
ret = false;
}
else { /* EOF is reached */
/* all files are successfully copied. */
if(isCancelled()) {
ret = false;
}
else {
/* some files are not copied */
if(n_children != n_copied) {
/* if the copy actions are skipped deliberately, it's ok */
if(!skip_dir_content) {
ret = false;
}
}
/* else job->skip_dir_content is true */
}
break;
}
}
}
g_file_enumerator_close(enu.get(), nullptr, &err);
}
}
return false;
}
bool CopyJob::makeDir(const FilePath& srcPath, GFileInfoPtr srcFile, const FilePath& dirPath) {
GError* err = nullptr;
if(isCancelled())
return false;
FilePath destPath = dirPath;
bool mkdir_done = false;
do {
mkdir_done = g_file_make_directory(destPath.gfile().get(), cancellable().get(), &err);
if(err->domain == G_IO_ERROR && (err->code == G_IO_ERROR_EXISTS ||
err->code == G_IO_ERROR_INVALID_FILENAME ||
err->code == G_IO_ERROR_FILENAME_TOO_LONG)) {
GFileInfoPtr destFile;
// FIXME: query its info
FilePath newDestPath;
FileExistsAction opt = askRename(FileInfo{srcFile, srcPath.parent()}, FileInfo{destFile, dirPath.parent()}, newDestPath);
g_error_free(err);
err = nullptr;
switch(opt) {
case FileOperationJob::RENAME:
destPath = newDestPath;
break;
case FileOperationJob::SKIP:
/* when a dir is skipped, we need to know its total size to calculate correct progress */
// job->finished += size;
// fm_file_ops_job_emit_percent(job);
// job->skip_dir_content = skip_dir_content = true;
mkdir_done = true; /* pretend that dir creation succeeded */
break;
case FileOperationJob::OVERWRITE:
mkdir_done = true; /* pretend that dir creation succeeded */
break;
case FileOperationJob::CANCEL:
cancel();
break;
case FileOperationJob::SKIP_ERROR: ; /* FIXME */
}
}
else {
#if 0
ErrorAction act = emitError( err, ErrorSeverity::MODERATE);
g_error_free(err);
err = nullptr;
if(act == ErrorAction::RETRY) {
goto _retry_mkdir;
}
#endif
break;
}
// job->finished += size;
} while(!mkdir_done && !isCancelled());
if(mkdir_done && !isCancelled()) {
bool chmod_done = false;
mode_t mode = g_file_info_get_attribute_uint32(srcFile.get(), G_FILE_ATTRIBUTE_UNIX_MODE);
if(mode) {
mode |= (S_IRUSR | S_IWUSR); /* ensure we have rw permission to this file. */
do {
/* chmod the newly created dir properly */
// if(!fm_job_is_cancelled(fmjob) && !job->skip_dir_content)
chmod_done = g_file_set_attribute_uint32(destPath.gfile().get(),
G_FILE_ATTRIBUTE_UNIX_MODE,
mode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err);
if(!chmod_done) {
/*
ErrorAction act = emitError( err, ErrorSeverity::MODERATE);
g_error_free(err);
err = nullptr;
if(act == ErrorAction::RETRY) {
goto _retry_chmod_for_dir;
}
*/
/* FIXME: some filesystems may not support this. */
}
} while(!chmod_done && !isCancelled());
// finished += size;
// fm_file_ops_job_emit_percent(job);
}
}
return false;
}
bool CopyJob::copyPath(const FilePath& srcPath, const FilePath& destDirPath, const char* destFileName) {
GErrorPtr err;
GFileInfoPtr srcInfo = GFileInfoPtr {
g_file_query_info(srcPath.gfile().get(),
gfile_info_query_attribs,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err),
false
};
if(!srcInfo || isCancelled()) {
return false;
}
return copyPath(srcPath, srcInfo, destDirPath, destFileName);
}
bool CopyJob::copyPath(const FilePath& srcPath, const GFileInfoPtr& srcInfo, const FilePath& destDirPath, const char* destFileName) {
setCurrentFile(srcPath);
GErrorPtr err;
GFileInfoPtr destDirInfo = GFileInfoPtr {
g_file_query_info(destDirPath.gfile().get(),
"id::filesystem",
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err),
false
};
if(!destDirInfo || isCancelled()) {
return false;
}
auto size = g_file_info_get_size(srcInfo.get());
setCurrentFileProgress(size, 0);
auto destPath = destDirPath.child(destFileName);
bool success = false;
switch(g_file_info_get_file_type(srcInfo.get())) {
case G_FILE_TYPE_DIRECTORY:
success = copyDir(srcPath, srcInfo, destPath);
break;
case G_FILE_TYPE_SPECIAL:
success = copySpecialFile(srcPath, srcInfo, destPath);
break;
default:
success = copyRegularFile(srcPath, srcInfo, destPath);
break;
}
if(success) {
addFinishedAmount(size, 1);
#if 0
if(ret && dest_folder) {
fm_dest = fm_path_new_for_gfile(dest);
if(!_fm_folder_event_file_added(dest_folder, fm_dest)) {
fm_path_unref(fm_dest);
}
}
#endif
}
return false;
}
#if 0
bool _fm_file_ops_job_copy_run(FmFileOpsJob* job) {
bool ret = true;
GFile* dest_dir;
GList* l;
FmJob* fmjob = FM_JOB(job);
/* prepare the job, count total work needed with FmDeepCountJob */
FmDeepCountJob* dc = fm_deep_count_job_new(job->srcs, FM_DC_JOB_DEFAULT);
FmFolder* df;
/* let the deep count job share the same cancellable object. */
fm_job_set_cancellable(FM_JOB(dc), fm_job_get_cancellable(fmjob));
fm_job_run_sync(FM_JOB(dc));
job->total = dc->total_size;
if(fm_job_is_cancelled(fmjob)) {
g_object_unref(dc);
return false;
}
g_object_unref(dc);
g_debug("total size to copy: %llu", (long long unsigned int)job->total);
dest_dir = fm_path_to_gfile(job->dest);
/* suspend updates for destination */
df = fm_folder_find_by_path(job->dest);
if(df) {
fm_folder_block_updates(df);
}
fm_file_ops_job_emit_prepared(job);
for(l = fm_path_list_peek_head_link(job->srcs); !fm_job_is_cancelled(fmjob) && l; l = l->next) {
FmPath* path = FM_PATH(l->data);
GFile* src = fm_path_to_gfile(path);
GFile* dest;
char* tmp_basename;
if(g_file_is_native(src) && g_file_is_native(dest_dir))
/* both are native */
{
tmp_basename = nullptr;
}
else if(g_file_is_native(src)) /* copy from native to virtual */
tmp_basename = g_filename_to_utf8(fm_path_get_basename(path),
-1, nullptr, nullptr, nullptr);
/* gvfs escapes it itself */
else { /* copy from virtual to native/virtual */
/* if we drop URI query onto native filesystem, omit query part */
const char* basename = fm_path_get_basename(path);
char* sub_name;
sub_name = strchr(basename, '?');
if(sub_name) {
sub_name = g_strndup(basename, sub_name - basename);
basename = strrchr(sub_name, G_DIR_SEPARATOR);
if(basename) {
basename++;
}
else {
basename = sub_name;
}
}
tmp_basename = fm_uri_subpath_to_native_subpath(basename, nullptr);
g_free(sub_name);
}
dest = g_file_get_child(dest_dir,
tmp_basename ? tmp_basename : fm_path_get_basename(path));
g_free(tmp_basename);
if(!_fm_file_ops_job_copy_file(job, src, nullptr, dest, nullptr, df)) {
ret = false;
}
g_object_unref(src);
g_object_unref(dest);
}
/* g_debug("finished: %llu, total: %llu", job->finished, job->total); */
fm_file_ops_job_emit_percent(job);
/* restore updates for destination */
if(df) {
fm_folder_unblock_updates(df);
g_object_unref(df);
}
g_object_unref(dest_dir);
return ret;
}
#endif
void CopyJob::exec() {
TotalSizeJob totalSizeJob{srcPaths_};
connect(&totalSizeJob, &TotalSizeJob::error, this, &CopyJob::error);
connect(this, &CopyJob::cancelled, &totalSizeJob, &TotalSizeJob::cancel);
totalSizeJob.run();
if(isCancelled()) {
return;
}
setTotalAmount(totalSizeJob.totalSize(), totalSizeJob.fileCount());
Q_EMIT preparedToRun();
for(auto& srcPath : srcPaths_) {
if(isCancelled()) {
break;
}
copyPath(srcPath, destDirPath_, srcPath.baseName().get());
}
}
} // namespace Fm

@ -1,46 +0,0 @@
#ifndef FM2_COPYJOB_H
#define FM2_COPYJOB_H
#include "../libfmqtglobals.h"
#include "fileoperationjob.h"
#include "gioptrs.h"
namespace Fm {
class LIBFM_QT_API CopyJob : public Fm::FileOperationJob {
Q_OBJECT
public:
enum class Mode {
COPY,
MOVE
};
explicit CopyJob(const FilePathList& paths, const FilePath& destDirPath, Mode mode = Mode::COPY);
explicit CopyJob(const FilePathList&& paths, const FilePath&& destDirPath, Mode mode = Mode::COPY);
protected:
void exec() override;
private:
bool copyPath(const FilePath& srcPath, const FilePath& destPath, const char *destFileName);
bool copyPath(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName);
bool copyRegularFile(const FilePath &srcPath, GFileInfoPtr srcFile, const FilePath& destPath);
bool copySpecialFile(const FilePath &srcPath, GFileInfoPtr srcFile, const FilePath& destPath);
bool copyDir(const FilePath &srcPath, GFileInfoPtr srcFile, const FilePath& destPath);
bool makeDir(const FilePath &srcPath, GFileInfoPtr srcFile, const FilePath& dirPath);
static void gfileProgressCallback(goffset current_num_bytes, goffset total_num_bytes, CopyJob* _this);
private:
FilePathList srcPaths_;
FilePath destDirPath_;
Mode mode_;
bool skip_dir_content;
};
} // namespace Fm
#endif // FM2_COPYJOB_H

@ -25,6 +25,9 @@ bool DeleteJob::deleteFile(const FilePath& path, GFileInfoPtr inf) {
}
}
// TODO: get parent dir of the current path.
// if there is a Fm::Folder object created for it, block the update for the folder temporarily.
/* currently processed file. */
setCurrentFile(path);
@ -33,11 +36,21 @@ bool DeleteJob::deleteFile(const FilePath& path, GFileInfoPtr inf) {
deleteDirContent(path, inf);
}
bool isTrashRoot = false;
// special handling for trash:///
if(!path.isNative() && g_strcmp0(path.uriScheme().get(), "trash") == 0) {
// little trick: basename of trash root is /
auto basename = path.baseName();
if(basename && basename[0] == G_DIR_SEPARATOR) {
isTrashRoot = true;
}
}
bool hasError = false;
while(!isCancelled()) {
GErrorPtr err;
// try to delete the path directly
if(g_file_delete(path.gfile().get(), cancellable().get(), &err)) {
// try to delete the path directly (but don't delete if it's trash:///)
if(isTrashRoot || g_file_delete(path.gfile().get(), cancellable().get(), &err)) {
break;
}
if(err) {
@ -69,26 +82,9 @@ bool DeleteJob::deleteFile(const FilePath& path, GFileInfoPtr inf) {
}
bool DeleteJob::deleteDirContent(const FilePath& path, GFileInfoPtr inf) {
#if 0
FmFolder* sub_folder;
/* special handling for trash:/// */
if(!g_file_is_native(gf)) {
char* scheme = g_file_get_uri_scheme(gf);
if(g_strcmp0(scheme, "trash") == 0) {
/* little trick: basename of trash root is /. */
char* basename = g_file_get_basename(gf);
if(basename && basename[0] == G_DIR_SEPARATOR) {
is_trash_root = true;
}
g_free(basename);
}
g_free(scheme);
}
#endif
GErrorPtr err;
GFileEnumeratorPtr enu {
g_file_enumerate_children(path.gfile().get(), gfile_info_query_attribs,
g_file_enumerate_children(path.gfile().get(), defaultGFileInfoQueryAttribs,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err),
false
@ -126,6 +122,17 @@ bool DeleteJob::deleteDirContent(const FilePath& path, GFileInfoPtr inf) {
}
DeleteJob::DeleteJob(const FilePathList &paths): paths_{paths} {
setCalcProgressUsingSize(false);
}
DeleteJob::DeleteJob(FilePathList &&paths): paths_{paths} {
setCalcProgressUsingSize(false);
}
DeleteJob::~DeleteJob() {
}
void DeleteJob::exec() {
/* prepare the job, count total work needed with FmDeepCountJob */
TotalSizeJob totalSizeJob{paths_, TotalSizeJob::Flags::PREPARE_DELETE};

@ -11,14 +11,11 @@ namespace Fm {
class LIBFM_QT_API DeleteJob : public Fm::FileOperationJob {
Q_OBJECT
public:
explicit DeleteJob(const FilePathList& paths): paths_{paths} {
}
explicit DeleteJob(const FilePathList& paths);
explicit DeleteJob(FilePathList&& paths): paths_{paths} {
}
explicit DeleteJob(FilePathList&& paths);
~DeleteJob() {
}
~DeleteJob();
protected:
void exec() override;

@ -25,7 +25,7 @@ void DirListJob::exec() {
_retry:
err.reset();
dir_inf = GFileInfoPtr{
g_file_query_info(dir_gfile.get(), gfile_info_query_attribs,
g_file_query_info(dir_gfile.get(), defaultGFileInfoQueryAttribs,
G_FILE_QUERY_INFO_NONE, cancellable().get(), &err),
false
};
@ -58,7 +58,7 @@ _retry:
// FIXME: _fm_file_info_job_update_fs_readonly(gf, inf, nullptr, nullptr);
err.reset();
GFileEnumeratorPtr enu = GFileEnumeratorPtr{
g_file_enumerate_children(dir_gfile.get(), gfile_info_query_attribs,
g_file_enumerate_children(dir_gfile.get(), defaultGFileInfoQueryAttribs,
G_FILE_QUERY_INFO_NONE, cancellable().get(), &err),
false
};

@ -1,9 +1,324 @@
#include "filechangeattrjob.h"
#include "totalsizejob.h"
#include <sys/stat.h>
namespace Fm {
FileChangeAttrJob::FileChangeAttrJob() {
static const char query[] = G_FILE_ATTRIBUTE_STANDARD_TYPE","
G_FILE_ATTRIBUTE_STANDARD_NAME","
G_FILE_ATTRIBUTE_UNIX_GID","
G_FILE_ATTRIBUTE_UNIX_UID","
G_FILE_ATTRIBUTE_UNIX_MODE","
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME;
FileChangeAttrJob::FileChangeAttrJob(FilePathList paths):
paths_{std::move(paths)},
recursive_{false},
// chmod
fileModeEnabled_{false},
newMode_{0},
newModeMask_{0},
// chown
ownerEnabled_{false},
uid_{0},
groupEnabled_{false},
gid_{0},
// Display name
displayNameEnabled_{false},
// icon
iconEnabled_{false},
// hidden
hiddenEnabled_{false},
hidden_{false},
// target uri
targetUriEnabled_{false} {
// the progress of chmod/chown is not related to file size
setCalcProgressUsingSize(false);
}
void FileChangeAttrJob::exec() {
// count total amount of the work
if(recursive_) {
TotalSizeJob totalSizeJob{paths_};
connect(&totalSizeJob, &TotalSizeJob::error, this, &FileChangeAttrJob::error);
connect(this, &FileChangeAttrJob::cancelled, &totalSizeJob, &TotalSizeJob::cancel);
totalSizeJob.run();
std::uint64_t totalSize, totalCount;
totalSizeJob.totalAmount(totalSize, totalCount);
setTotalAmount(totalSize, totalCount);
}
else {
setTotalAmount(paths_.size(), paths_.size());
}
// ready to start
Q_EMIT preparedToRun();
// do the actual change attrs job
for(auto& path : paths_) {
if(isCancelled()) {
break;
}
GErrorPtr err;
GFileInfoPtr info{
g_file_query_info(path.gfile().get(), query,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err),
false
};
if(info) {
processFile(path, info);
}
else {
handleError(err, path, info);
}
}
}
bool FileChangeAttrJob::processFile(const FilePath& path, const GFileInfoPtr& info) {
setCurrentFile(path);
bool ret = true;
if(ownerEnabled_) {
changeFileOwner(path, info, uid_);
}
if(groupEnabled_) {
changeFileGroup(path, info, gid_);
}
if(fileModeEnabled_) {
changeFileMode(path, info, newMode_, newModeMask_);
}
/* change display name, icon, hidden, target */
if(displayNameEnabled_ && !displayName().empty()) {
changeFileDisplayName(path, info, displayName_.c_str());
}
if(iconEnabled_ && icon_) {
changeFileIcon(path, info, icon_);
}
if(hiddenEnabled_) {
changeFileHidden(path, info, hidden_);
}
if(targetUriEnabled_ && !targetUri_.empty()) {
changeFileTargetUri(path, info, targetUri_.c_str());
}
// FIXME: do not use size 1 here.
addFinishedAmount(1, 1);
// recursively apply to subfolders
auto type = g_file_info_get_file_type(info.get());
if(!isCancelled() && recursive_ && type == G_FILE_TYPE_DIRECTORY) {
bool retry;
do {
retry = false;
GErrorPtr err;
GFileEnumeratorPtr enu{
g_file_enumerate_children(path.gfile().get(), query,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err),
false
};
if(enu) {
while(!isCancelled()) {
err.reset();
GFileInfoPtr childInfo{g_file_enumerator_next_file(enu.get(), cancellable().get(), &err), false};
if(childInfo) {
auto childPath = path.child(g_file_info_get_name(childInfo.get()));
ret = processFile(childPath, childInfo);
if(!ret) { /* _fm_file_ops_job_change_attr_file() failed */
break;
}
}
else {
if(err) {
handleError(err, path, info, ErrorSeverity::MILD);
retry = false;
/* FM_JOB_RETRY is not supported here */
}
else { /* EOF */
break;
}
}
}
g_file_enumerator_close(enu.get(), cancellable().get(), nullptr);
}
else {
retry = handleError(err, path, info);
}
} while(!isCancelled() && retry);
}
return ret;
}
bool FileChangeAttrJob::handleError(GErrorPtr &err, const FilePath &path, const GFileInfoPtr &info, ErrorSeverity severity) {
auto act = emitError(err, severity);
if (act == ErrorAction::RETRY) {
err.reset();
return true;
}
return false;
}
bool FileChangeAttrJob::changeFileOwner(const FilePath& path, const GFileInfoPtr& info, uid_t uid) {
/* change owner */
bool ret = false;
bool retry;
do {
GErrorPtr err;
if(!g_file_set_attribute_uint32(path.gfile().get(), G_FILE_ATTRIBUTE_UNIX_UID,
uid, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err)) {
retry = handleError(err, path, info, ErrorSeverity::MILD);
err.reset();
}
else {
ret = true;
retry = false;
}
} while(retry && !isCancelled());
return ret;
}
bool FileChangeAttrJob::changeFileGroup(const FilePath& path, const GFileInfoPtr& info, gid_t gid) {
/* change group */
bool ret = false;
bool retry;
do {
GErrorPtr err;
if(!g_file_set_attribute_uint32(path.gfile().get(), G_FILE_ATTRIBUTE_UNIX_GID,
gid, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err)) {
retry = handleError(err, path, info, ErrorSeverity::MILD);
err.reset();
}
else {
ret = true;
retry = false;
}
} while(retry && !isCancelled());
return ret;
}
bool FileChangeAttrJob::changeFileMode(const FilePath& path, const GFileInfoPtr& info, mode_t newMode, mode_t newModeMask) {
bool ret = false;
/* change mode */
if(newModeMask) {
guint32 mode = g_file_info_get_attribute_uint32(info.get(), G_FILE_ATTRIBUTE_UNIX_MODE);
mode &= ~newModeMask;
mode |= (newMode & newModeMask);
auto type = g_file_info_get_file_type(info.get());
/* FIXME: this behavior should be optional. */
/* treat dirs with 'r' as 'rx' */
if(type == G_FILE_TYPE_DIRECTORY) {
if((newModeMask & S_IRUSR) && (mode & S_IRUSR)) {
mode |= S_IXUSR;
}
if((newModeMask & S_IRGRP) && (mode & S_IRGRP)) {
mode |= S_IXGRP;
}
if((newModeMask & S_IROTH) && (mode & S_IROTH)) {
mode |= S_IXOTH;
}
}
/* new mode */
bool retry;
do {
GErrorPtr err;
if(!g_file_set_attribute_uint32(path.gfile().get(), G_FILE_ATTRIBUTE_UNIX_MODE,
mode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err)) {
retry = handleError(err, path, info, ErrorSeverity::MILD);
err.reset();
}
else {
ret = true;
retry = false;
}
} while(retry && !isCancelled());
}
return ret;
}
bool FileChangeAttrJob::changeFileDisplayName(const FilePath& path, const GFileInfoPtr& info, const char* displayName) {
bool ret = false;
bool retry;
do {
GErrorPtr err;
if(!g_file_set_display_name(path.gfile().get(), displayName, cancellable().get(), &err)) {
retry = handleError(err, path, info, ErrorSeverity::MILD);
err.reset();
}
else {
ret = true;
retry = false;
}
} while(retry && !isCancelled());
return ret;
}
bool FileChangeAttrJob::changeFileIcon(const FilePath& path, const GFileInfoPtr& info, GIconPtr& icon) {
bool ret = false;
bool retry;
do {
GErrorPtr err;
if(!g_file_set_attribute(path.gfile().get(), G_FILE_ATTRIBUTE_STANDARD_ICON,
G_FILE_ATTRIBUTE_TYPE_OBJECT, icon.get(),
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err)) {
retry = handleError(err, path, info, ErrorSeverity::MILD);
err.reset();
}
else {
ret = true;
retry = false;
}
} while(retry && !isCancelled());
return ret;
}
bool FileChangeAttrJob::changeFileHidden(const FilePath& path, const GFileInfoPtr& info, bool hidden) {
bool ret = false;
bool retry;
do {
GErrorPtr err;
gboolean g_hidden = hidden;
if(!g_file_set_attribute(path.gfile().get(), G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
G_FILE_ATTRIBUTE_TYPE_BOOLEAN, &g_hidden,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err)) {
retry = handleError(err, path, info, ErrorSeverity::MILD);
err.reset();
}
else {
ret = true;
retry = false;
}
} while(retry && !isCancelled());
return ret;
}
bool FileChangeAttrJob::changeFileTargetUri(const FilePath& path, const GFileInfoPtr& info, const char* targetUri) {
bool ret = false;
bool retry;
do {
GErrorPtr err;
if(!g_file_set_attribute_string(path.gfile().get(), G_FILE_ATTRIBUTE_STANDARD_TARGET_URI,
targetUri, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err)) {
retry = handleError(err, path, info, ErrorSeverity::MILD);
err.reset();
}
else {
ret = true;
retry = false;
}
} while(retry && !isCancelled());
return ret;
}
} // namespace Fm

@ -3,13 +3,141 @@
#include "../libfmqtglobals.h"
#include "fileoperationjob.h"
#include "gioptrs.h"
#include <string>
#include <sys/types.h>
namespace Fm {
class LIBFM_QT_API FileChangeAttrJob : public Fm::FileOperationJob {
Q_OBJECT
public:
explicit FileChangeAttrJob();
explicit FileChangeAttrJob(FilePathList paths);
void setFileModeEnabled(bool enabled) {
fileModeEnabled_ = enabled;
}
void setFileMode(mode_t newMode, mode_t newModeMask) {
newMode_ = newMode;
newModeMask_ = newModeMask;
}
bool ownerEnabled() const {
return ownerEnabled_;
}
void setOwnerEnabled(bool enabled) {
ownerEnabled_ = enabled;
}
void setOwner(uid_t uid) {
uid_ = uid;
}
bool groupEnabled() const {
return groupEnabled_;
}
void setGroupEnabled(bool groupEnabled) {
groupEnabled_ = groupEnabled;
}
void setGroup(gid_t gid) {
gid_ = gid;
}
// This only work for change attr jobs.
void setRecursive(bool recursive) {
recursive_ = recursive;
}
void setHiddenEnabled(bool enabled) {
hiddenEnabled_ = enabled;
}
void setHidden(bool hidden) {
hidden_ = hidden;
}
bool iconEnabled() const {
return iconEnabled_;
}
void setIconEnabled(bool iconEnabled) {
iconEnabled_ = iconEnabled;
}
bool displayNameEnabled() const {
return displayNameEnabled_;
}
void setDisplayNameEnabled(bool displayNameEnabled) {
displayNameEnabled_ = displayNameEnabled;
}
const std::string& displayName() const {
return displayName_;
}
void setDisplayName(const std::string& displayName) {
displayName_ = displayName;
}
bool targetUriEnabled() const {
return targetUriEnabled_;
}
void setTargetUriEnabled(bool targetUriEnabled) {
targetUriEnabled_ = targetUriEnabled;
}
const std::string& targetUri() const {
return targetUri_;
}
void setTargetUri(const std::string& value) {
targetUri_ = value;
}
protected:
void exec() override;
private:
bool processFile(const FilePath& path, const GFileInfoPtr& info);
bool handleError(GErrorPtr& err, const FilePath& path, const GFileInfoPtr& info, ErrorSeverity severity = ErrorSeverity::MODERATE);
bool changeFileOwner(const FilePath& path, const GFileInfoPtr& info, uid_t uid);
bool changeFileGroup(const FilePath& path, const GFileInfoPtr& info, gid_t gid);
bool changeFileMode(const FilePath& path, const GFileInfoPtr& info, mode_t newMode, mode_t newModeMask);
bool changeFileDisplayName(const FilePath& path, const GFileInfoPtr& info, const char* displayName);
bool changeFileIcon(const FilePath& path, const GFileInfoPtr& info, GIconPtr& icon);
bool changeFileHidden(const FilePath& path, const GFileInfoPtr& info, bool hidden);
bool changeFileTargetUri(const FilePath& path, const GFileInfoPtr& info, const char* targetUri_);
private:
FilePathList paths_;
bool recursive_;
// chmod
bool fileModeEnabled_;
mode_t newMode_;
mode_t newModeMask_;
// chown
bool ownerEnabled_;
uid_t uid_;
bool groupEnabled_;
gid_t gid_;
// Display name
bool displayNameEnabled_;
std::string displayName_;
// icon
bool iconEnabled_;
GIconPtr icon_;
// hidden
bool hiddenEnabled_;
bool hidden_;
// target uri
bool targetUriEnabled_;
std::string targetUri_;
};
} // namespace Fm

@ -4,12 +4,12 @@
namespace Fm {
const char gfile_info_query_attribs[] = "standard::*,"
"unix::*,"
"time::*,"
"access::*,"
"id::filesystem,"
"metadata::emblems";
const char defaultGFileInfoQueryAttribs[] = "standard::*,"
"unix::*,"
"time::*,"
"access::*,"
"id::filesystem,"
"metadata::emblems";
FileInfo::FileInfo() {
// FIXME: initialize numeric data members
@ -28,7 +28,8 @@ void FileInfo::setFromGFileInfo(const GObjectPtr<GFileInfo>& inf, const FilePath
GIcon* gicon;
GFileType type;
name_ = g_file_info_get_name(inf.get());
if (const char * name = g_file_info_get_name(inf.get()))
name_ = name;
dispName_ = g_file_info_get_display_name(inf.get());
@ -118,6 +119,8 @@ void FileInfo::setFromGFileInfo(const GObjectPtr<GFileInfo>& inf, const FilePath
isDeletable_ = true;
}
isShortcut_ = false;
/* special handling for symlinks */
if(g_file_info_get_is_symlink(inf.get())) {
mode_ &= ~S_IFMT; /* reset type */
@ -125,11 +128,10 @@ void FileInfo::setFromGFileInfo(const GObjectPtr<GFileInfo>& inf, const FilePath
goto _file_is_symlink;
}
isShortcut_ = false;
switch(type) {
case G_FILE_TYPE_SHORTCUT:
isShortcut_ = true;
/* Falls through. */
case G_FILE_TYPE_MOUNTABLE:
uri = g_file_info_get_attribute_string(inf.get(), G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
if(uri) {
@ -184,6 +186,7 @@ _file_is_symlink:
mimeType_ = MimeType::guessFromFileName(target_.c_str());
}
}
/* Falls through. */
/* continue with absent mime type */
default: /* G_FILE_TYPE_UNKNOWN G_FILE_TYPE_REGULAR G_FILE_TYPE_SPECIAL */
if(G_UNLIKELY(!mimeType_)) {
@ -211,7 +214,7 @@ _file_is_symlink:
g_key_file_free(kf);
}
}
if(!icon_) {
/* try file-specific icon first */
gicon = g_file_info_get_icon(inf.get());
@ -246,7 +249,11 @@ _file_is_symlink:
atime_ = g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_TIME_ACCESS);
ctime_ = g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_TIME_CHANGED);
isHidden_ = g_file_info_get_is_hidden(inf.get());
isBackup_ = g_file_info_get_is_backup(inf.get());
// g_file_info_get_is_backup() does not cover ".bak" and ".old".
// NOTE: Here, dispName_ is not modified for desktop entries yet.
isBackup_ = g_file_info_get_is_backup(inf.get())
|| dispName_.endsWith(QLatin1String(".bak"))
|| dispName_.endsWith(QLatin1String(".old"));
isNameChangeable_ = true; /* GVFS tends to ignore this attribute */
isIconChangeable_ = isHiddenChangeable_ = false;
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME)) {
@ -326,7 +333,31 @@ bool FileInfo::canThumbnail() const {
/* full path of the file is required by this function */
bool FileInfo::isExecutableType() const {
if(isText()) { /* g_content_type_can_be_executable reports text files as executables too */
if(isDesktopEntry()) {
/* treat desktop entries as executables if
they are native and have read permission */
if(isNative() && (mode_ & (S_IRUSR|S_IRGRP|S_IROTH))) {
if(isShortcut() && !target_.empty()) {
/* handle shortcuts from desktop to menu entries:
first check for entries in /usr/share/applications and such
which may be considered as a safe desktop entry path
then check if that is a shortcut to a native file
otherwise it is a link to a file under menu:// */
if (!g_str_has_prefix(target_.c_str(), "/usr/share/")) {
auto target = FilePath::fromPathStr(target_.c_str());
bool is_native = target.isNative();
if (is_native) {
return true;
}
}
}
else {
return true;
}
}
return false;
}
else if(isText()) { /* g_content_type_can_be_executable reports text files as executables too */
/* We don't execute remote files nor files in trash */
if(isNative() && (mode_ & (S_IXOTH | S_IXGRP | S_IXUSR))) {
/* it has executable bits so lets check shell-bang */

@ -90,16 +90,16 @@ public:
return mimeType_;
}
time_t ctime() const {
quint64 ctime() const {
return ctime_;
}
time_t atime() const {
quint64 atime() const {
return atime_;
}
time_t mtime() const {
quint64 mtime() const {
return mtime_;
}
@ -163,7 +163,7 @@ public:
}
bool isDir() const {
return mimeType_->isDir();
return S_ISDIR(mode_) || mimeType_->isDir();
}
bool isNative() const {
@ -194,6 +194,10 @@ public:
return dispName_;
}
QString description() const {
return QString::fromUtf8(mimeType_ ? mimeType_->desc() : "");
}
FilePath path() const {
return dirPath_ ? dirPath_.child(name_.c_str()) : FilePath::fromPathStr(name_.c_str());
}
@ -221,9 +225,9 @@ private:
uid_t uid_;
gid_t gid_;
uint64_t size_;
time_t mtime_;
time_t atime_;
time_t ctime_;
quint64 mtime_;
quint64 atime_;
quint64 ctime_;
uint64_t blksize_;
uint64_t blocks_;
@ -266,9 +270,10 @@ public:
}
};
// smart pointer to FileInfo objects (once created, FileInfo objects should stay immutable so const is needed here)
typedef std::shared_ptr<const FileInfo> FileInfoPtr;
typedef std::pair<std::shared_ptr<const FileInfo>, std::shared_ptr<const FileInfo>> FileInfoPair;
typedef std::pair<FileInfoPtr, FileInfoPtr> FileInfoPair;
}

@ -3,7 +3,7 @@
namespace Fm {
extern const char gfile_info_query_attribs[];
extern const char defaultGFileInfoQueryAttribs[];
} // namespace Fm

@ -3,9 +3,10 @@
namespace Fm {
FileInfoJob::FileInfoJob(FilePathList paths, FilePath commonDirPath, const std::shared_ptr<const HashSet>& cutFilesHashSet):
FileInfoJob::FileInfoJob(FilePathList paths, FilePathList deletionPaths, FilePath commonDirPath, const std::shared_ptr<const HashSet>& cutFilesHashSet):
Job(),
paths_{std::move(paths)},
deletionPaths_{std::move(deletionPaths)},
commonDirPath_{std::move(commonDirPath)},
cutFilesHashSet_{cutFilesHashSet} {
}
@ -15,12 +16,13 @@ void FileInfoJob::exec() {
if(!isCancelled()) {
GErrorPtr err;
GFileInfoPtr inf{
g_file_query_info(path.gfile().get(), gfile_info_query_attribs,
g_file_query_info(path.gfile().get(), defaultGFileInfoQueryAttribs,
G_FILE_QUERY_INFO_NONE, cancellable().get(), &err),
false
};
if(!inf)
return;
if(!inf) {
continue;
}
// Reuse the same dirPath object when the path remains the same (optimize for files in the same dir)
auto dirPath = commonDirPath_.isValid() ? commonDirPath_ : path.parent();

@ -13,12 +13,16 @@ class LIBFM_QT_API FileInfoJob : public Job {
Q_OBJECT
public:
explicit FileInfoJob(FilePathList paths, FilePath commonDirPath = FilePath(), const std::shared_ptr<const HashSet>& cutFilesHashSet = nullptr);
explicit FileInfoJob(FilePathList paths, FilePathList deletionPaths = FilePathList(), FilePath commonDirPath = FilePath(), const std::shared_ptr<const HashSet>& cutFilesHashSet = nullptr);
const FilePathList& paths() const {
return paths_;
}
const FilePathList& deletionPaths() const {
return deletionPaths_;
}
const FileInfoList& files() const {
return results_;
}
@ -31,6 +35,7 @@ protected:
private:
FilePathList paths_;
FilePathList deletionPaths_;
FileInfoList results_;
FilePath commonDirPath_;
const std::shared_ptr<const HashSet> cutFilesHashSet_;

@ -4,6 +4,7 @@ namespace Fm {
FileOperationJob::FileOperationJob():
hasTotalAmount_{false},
calcProgressUsingSize_{true},
totalSize_{0},
totalCount_{0},
finishedSize_{0},
@ -31,6 +32,22 @@ bool FileOperationJob::currentFileProgress(FilePath& path, uint64_t& totalSize,
return currentFile_.isValid();
}
double FileOperationJob::progress() const {
std::lock_guard<std::mutex> lock{mutex_};
double finishedRatio;
if(calcProgressUsingSize_) {
finishedRatio = totalSize_ > 0 ? double(finishedSize_ + currentFileFinished_) / totalSize_ : 0.0;
}
else {
finishedRatio = totalCount_ > 0 ? double(finishedCount_) / totalCount_ : 0.0;
}
if(finishedRatio > 1.0) {
finishedRatio = 1.0;
}
return finishedRatio;
}
FileOperationJob::FileExistsAction FileOperationJob::askRename(const FileInfo &src, const FileInfo &dest, FilePath &newDest) {
FileExistsAction action = SKIP;
Q_EMIT fileExists(src, dest, action, newDest);
@ -47,31 +64,37 @@ bool FileOperationJob::finishedAmount(uint64_t& finishedSize, uint64_t& finished
}
void FileOperationJob::setTotalAmount(uint64_t fileSize, uint64_t fileCount) {
std::lock_guard<std::mutex> locl{mutex_};
std::lock_guard<std::mutex> lock{mutex_};
hasTotalAmount_ = true;
totalSize_ = fileSize;
totalCount_ = fileCount;
}
void FileOperationJob::setFinishedAmount(uint64_t finishedSize, uint64_t finishedCount) {
std::lock_guard<std::mutex> locl{mutex_};
std::lock_guard<std::mutex> lock{mutex_};
finishedSize_ = finishedSize;
finishedCount_ = finishedCount;
}
void FileOperationJob::addFinishedAmount(uint64_t finishedSize, uint64_t finishedCount) {
std::lock_guard<std::mutex> locl{mutex_};
std::lock_guard<std::mutex> lock{mutex_};
finishedSize_ += finishedSize;
finishedCount_ += finishedCount;
}
FilePath FileOperationJob::currentFile() const {
std::lock_guard<std::mutex> lock{mutex_};
auto ret = currentFile_;
return ret;
}
void FileOperationJob::setCurrentFile(const FilePath& path) {
std::lock_guard<std::mutex> locl{mutex_};
std::lock_guard<std::mutex> lock{mutex_};
currentFile_ = path;
}
void FileOperationJob::setCurrentFileProgress(uint64_t totalSize, uint64_t finishedSize) {
std::lock_guard<std::mutex> locl{mutex_};
std::lock_guard<std::mutex> lock{mutex_};
currentFileSize_ = totalSize;
currentFileFinished_ = finishedSize;
}

@ -24,12 +24,26 @@ public:
explicit FileOperationJob();
// get total amount of work to do
bool totalAmount(std::uint64_t& fileSize, std::uint64_t& fileCount) const;
// get currently finished job amount
bool finishedAmount(std::uint64_t& finishedSize, std::uint64_t& finishedCount) const;
// get the current file
FilePath currentFile() const;
// get progress of the current file
bool currentFileProgress(FilePath& path, std::uint64_t& totalSize, std::uint64_t& finishedSize) const;
// is the job calculate progress based on file size or file counts
bool calcProgressUsingSize() const {
return calcProgressUsingSize_;
}
// get currently finished amount (0.0 to 1.0)
virtual double progress() const;
Q_SIGNALS:
void preparedToRun();
@ -55,8 +69,17 @@ protected:
void setCurrentFileProgress(uint64_t totalSize, uint64_t finishedSize);
void setCalcProgressUsingSize(bool value) {
calcProgressUsingSize_ = value;
}
std::mutex& mutex() {
return mutex_;
}
private:
bool hasTotalAmount_;
bool calcProgressUsingSize_;
std::uint64_t totalSize_;
std::uint64_t totalCount_;
std::uint64_t finishedSize_;

@ -0,0 +1,641 @@
#include "filetransferjob.h"
#include "totalsizejob.h"
#include "fileinfo_p.h"
namespace Fm {
FileTransferJob::FileTransferJob(FilePathList srcPaths, Mode mode):
FileOperationJob{},
srcPaths_{std::move(srcPaths)},
mode_{mode} {
}
FileTransferJob::FileTransferJob(FilePathList srcPaths, FilePathList destPaths, Mode mode):
FileTransferJob{std::move(srcPaths), mode} {
destPaths_ = std::move(destPaths);
}
FileTransferJob::FileTransferJob(FilePathList srcPaths, const FilePath& destDirPath, Mode mode):
FileTransferJob{std::move(srcPaths), mode} {
setDestDirPath(destDirPath);
}
void FileTransferJob::setSrcPaths(FilePathList srcPaths) {
srcPaths_ = std::move(srcPaths);
}
void FileTransferJob::setDestPaths(FilePathList destPaths) {
destPaths_ = std::move(destPaths);
}
void FileTransferJob::setDestDirPath(const FilePath& destDirPath) {
destPaths_.clear();
destPaths_.reserve(srcPaths_.size());
for(const auto& srcPath: srcPaths_) {
FilePath destPath;
if(mode_ == Mode::LINK && !srcPath.isNative()) {
// special handling for URLs
auto fullBasename = srcPath.baseName();
char* basename = fullBasename.get();
char* dname = nullptr;
// if we drop URI query onto native filesystem, omit query part
if(!srcPath.isNative()) {
dname = strchr(basename, '?');
}
// if basename consist only from query then use first part of it
if(dname == basename) {
basename++;
dname = strchr(basename, '&');
}
CStrPtr _basename;
if(dname) {
_basename = CStrPtr{g_strndup(basename, dname - basename)};
dname = strrchr(_basename.get(), G_DIR_SEPARATOR);
g_debug("cutting '%s' to '%s'", basename, dname ? &dname[1] : _basename.get());
if(dname) {
basename = &dname[1];
}
else {
basename = _basename.get();
}
}
destPath = destDirPath.child(basename);
}
else {
destPath = destDirPath.child(srcPath.baseName().get());
}
destPaths_.emplace_back(std::move(destPath));
}
}
void FileTransferJob::gfileCopyProgressCallback(goffset current_num_bytes, goffset total_num_bytes, FileTransferJob* _this) {
_this->setCurrentFileProgress(total_num_bytes, current_num_bytes);
}
bool FileTransferJob::moveFileSameFs(const FilePath& srcPath, const GFileInfoPtr& srcInfo, FilePath& destPath) {
int flags = G_FILE_COPY_ALL_METADATA | G_FILE_COPY_NOFOLLOW_SYMLINKS;
GErrorPtr err;
bool retry;
do {
retry = false;
err.reset();
// do the file operation
if(!g_file_move(srcPath.gfile().get(), destPath.gfile().get(), GFileCopyFlags(flags), cancellable().get(),
nullptr, this, &err)) {
retry = handleError(err, srcPath, srcInfo, destPath, flags);
}
else {
return true;
}
} while(retry && !isCancelled());
return false;
}
bool FileTransferJob::copyRegularFile(const FilePath& srcPath, const GFileInfoPtr& srcInfo, FilePath& destPath) {
int flags = G_FILE_COPY_ALL_METADATA | G_FILE_COPY_NOFOLLOW_SYMLINKS;
GErrorPtr err;
bool retry;
do {
retry = false;
err.reset();
// reset progress of the current file (only for copy)
auto size = g_file_info_get_size(srcInfo.get());
setCurrentFileProgress(size, 0);
// do the file operation
if(!g_file_copy(srcPath.gfile().get(), destPath.gfile().get(), GFileCopyFlags(flags), cancellable().get(),
(GFileProgressCallback)&gfileCopyProgressCallback, this, &err)) {
retry = handleError(err, srcPath, srcInfo, destPath, flags);
}
else {
return true;
}
} while(retry && !isCancelled());
return false;
}
bool FileTransferJob::copySpecialFile(const FilePath& srcPath, const GFileInfoPtr& srcInfo, FilePath &destPath) {
bool ret = false;
// only handle FIFO for local files
if(srcPath.isNative() && destPath.isNative()) {
auto src_path = srcPath.localPath();
struct stat src_st;
int r;
r = lstat(src_path.get(), &src_st);
if(r == 0) {
// Handle FIFO on native file systems.
if(S_ISFIFO(src_st.st_mode)) {
auto dest_path = destPath.localPath();
if(mkfifo(dest_path.get(), src_st.st_mode) == 0) {
ret = true;
}
}
// FIXME: how about block device, char device, and socket?
}
}
if(!ret) {
GErrorPtr err;
g_set_error(&err, G_IO_ERROR, G_IO_ERROR_FAILED,
("Cannot copy file '%s': not supported"),
g_file_info_get_display_name(srcInfo.get()));
emitError(err, ErrorSeverity::MODERATE);
}
return ret;
}
bool FileTransferJob::copyDirContent(const FilePath& srcPath, GFileInfoPtr srcInfo, FilePath& destPath, bool skip) {
bool ret = false;
// copy dir content
GErrorPtr err;
auto enu = GFileEnumeratorPtr{
g_file_enumerate_children(srcPath.gfile().get(),
defaultGFileInfoQueryAttribs,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err),
false};
if(enu) {
int n_children = 0;
int n_copied = 0;
ret = true;
while(!isCancelled()) {
err.reset();
GFileInfoPtr inf{g_file_enumerator_next_file(enu.get(), cancellable().get(), &err), false};
if(inf) {
++n_children;
const char* name = g_file_info_get_name(inf.get());
FilePath childPath = srcPath.child(name);
bool child_ret = copyFile(childPath, inf, destPath, name, skip);
if(child_ret) {
++n_copied;
}
else {
ret = false;
}
}
else {
if(err) {
// fail to read directory content
// NOTE: since we cannot read the source dir, we cannot calculate the progress correctly, either.
emitError(err, ErrorSeverity::MODERATE);
err.reset();
/* ErrorAction::RETRY is not supported here */
ret = false;
}
else { /* EOF is reached */
/* all files are successfully copied. */
if(isCancelled()) {
ret = false;
}
else {
/* some files are not copied */
if(n_children != n_copied) {
/* if the copy actions are skipped deliberately, it's ok */
if(!skip) {
ret = false;
}
}
/* else job->skip_dir_content is true */
}
break;
}
}
}
g_file_enumerator_close(enu.get(), nullptr, &err);
}
else {
if(err) {
emitError(err, ErrorSeverity::MODERATE);
}
}
return ret;
}
bool FileTransferJob::makeDir(const FilePath& srcPath, GFileInfoPtr srcInfo, FilePath& destPath) {
if(isCancelled()) {
return false;
}
bool mkdir_done = false;
do {
GErrorPtr err;
mkdir_done = g_file_make_directory_with_parents(destPath.gfile().get(), cancellable().get(), &err);
if(!mkdir_done) {
if(err->domain == G_IO_ERROR && (err->code == G_IO_ERROR_EXISTS ||
err->code == G_IO_ERROR_INVALID_FILENAME ||
err->code == G_IO_ERROR_FILENAME_TOO_LONG)) {
GFileInfoPtr destInfo = GFileInfoPtr {
g_file_query_info(destPath.gfile().get(),
defaultGFileInfoQueryAttribs,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), nullptr),
false
};
if(!destInfo) {
// FIXME: error handling
break;
}
FilePath newDestPath;
FileExistsAction opt = askRename(FileInfo{srcInfo, srcPath.parent()}, FileInfo{destInfo, destPath.parent()}, newDestPath);
switch(opt) {
case FileOperationJob::RENAME:
destPath = std::move(newDestPath);
break;
case FileOperationJob::SKIP:
/* when a dir is skipped, we need to know its total size to calculate correct progress */
mkdir_done = true; /* pretend that dir creation succeeded */
break;
case FileOperationJob::OVERWRITE:
mkdir_done = true; /* pretend that dir creation succeeded */
break;
case FileOperationJob::CANCEL:
cancel();
return false;
case FileOperationJob::SKIP_ERROR: ; /* FIXME */
}
}
else {
ErrorAction act = emitError(err, ErrorSeverity::MODERATE);
if(act != ErrorAction::RETRY) {
break;
}
}
}
} while(!mkdir_done && !isCancelled());
bool chmod_done = false;
if(mkdir_done && !isCancelled()) {
mode_t mode = g_file_info_get_attribute_uint32(srcInfo.get(), G_FILE_ATTRIBUTE_UNIX_MODE);
if(mode) {
mode |= (S_IRUSR | S_IWUSR); /* ensure we have rw permission to this file. */
do {
GErrorPtr err;
// chmod the newly created dir properly
// if(!fm_job_is_cancelled(fmjob) && !job->skip_dir_content)
chmod_done = g_file_set_attribute_uint32(destPath.gfile().get(),
G_FILE_ATTRIBUTE_UNIX_MODE,
mode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err);
if(!chmod_done) {
ErrorAction act = emitError(err, ErrorSeverity::MODERATE);
if(act != ErrorAction::RETRY) {
break;
}
/* FIXME: some filesystems may not support this. */
}
} while(!chmod_done && !isCancelled());
}
}
return mkdir_done && chmod_done;
}
bool FileTransferJob::handleError(GErrorPtr &err, const FilePath &srcPath, const GFileInfoPtr &srcInfo, FilePath &destPath, int& flags) {
bool retry = false;
/* handle existing files or file name conflict */
if(err.domain() == G_IO_ERROR && (err.code() == G_IO_ERROR_EXISTS ||
err.code() == G_IO_ERROR_INVALID_FILENAME ||
err.code() == G_IO_ERROR_FILENAME_TOO_LONG)) {
flags &= ~G_FILE_COPY_OVERWRITE;
// get info of the existing file
GFileInfoPtr destInfo = GFileInfoPtr {
g_file_query_info(destPath.gfile().get(),
defaultGFileInfoQueryAttribs,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), nullptr),
false
};
// ask the user to rename or overwrite the existing file
if(!isCancelled() && destInfo) {
FilePath newDestPath;
FileExistsAction opt = askRename(FileInfo{srcInfo, srcPath.parent()},
FileInfo{destInfo, destPath.parent()},
newDestPath);
switch(opt) {
case FileOperationJob::RENAME:
// try a new file name
if(newDestPath.isValid()) {
destPath = std::move(newDestPath);
// FIXME: handle the error when newDestPath is invalid.
}
retry = true;
break;
case FileOperationJob::OVERWRITE:
// overwrite existing file
flags |= G_FILE_COPY_OVERWRITE;
retry = true;
err.reset();
break;
case FileOperationJob::CANCEL:
// cancel the whole job.
cancel();
break;
case FileOperationJob::SKIP:
// skip current file and don't copy it
case FileOperationJob::SKIP_ERROR: ; /* FIXME */
retry = false;
break;
}
err.reset();
}
}
// show error message
if(!isCancelled() && err) {
ErrorAction act = emitError(err, ErrorSeverity::MODERATE);
err.reset();
if(act == ErrorAction::RETRY) {
// the user wants retry the operation again
retry = true;
}
const bool is_no_space = (err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_NO_SPACE);
/* FIXME: ask to leave partial content? */
if(is_no_space) {
// run out of disk space. delete the partial content we copied.
g_file_delete(destPath.gfile().get(), cancellable().get(), nullptr);
}
}
return retry;
}
bool FileTransferJob::processPath(const FilePath& srcPath, const FilePath& destDirPath, const char* destFileName) {
GErrorPtr err;
GFileInfoPtr srcInfo = GFileInfoPtr {
g_file_query_info(srcPath.gfile().get(),
defaultGFileInfoQueryAttribs,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err),
false
};
if(!srcInfo || isCancelled()) {
// FIXME: report error
return false;
}
bool ret;
switch(mode_) {
case Mode::MOVE:
ret = moveFile(srcPath, srcInfo, destDirPath, destFileName);
break;
case Mode::COPY: {
bool deleteSrc = false;
ret = copyFile(srcPath, srcInfo, destDirPath, destFileName, deleteSrc);
break;
}
case Mode::LINK:
ret = linkFile(srcPath, srcInfo, destDirPath, destFileName);
break;
default:
ret = false;
break;
}
return ret;
}
bool FileTransferJob::moveFile(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName) {
setCurrentFile(srcPath);
GErrorPtr err;
GFileInfoPtr destDirInfo = GFileInfoPtr {
g_file_query_info(destDirPath.gfile().get(),
"id::filesystem",
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(), &err),
false
};
if(!destDirInfo || isCancelled()) {
// FIXME: report errors
return false;
}
// If src and dest are on the same filesystem, do move.
// Exception: if src FS is trash:///, we always do move
// Otherwise, do copy & delete src files.
auto src_fs = g_file_info_get_attribute_string(srcInfo.get(), "id::filesystem");
auto dest_fs = g_file_info_get_attribute_string(destDirInfo.get(), "id::filesystem");
bool ret;
if(src_fs && dest_fs && (strcmp(src_fs, dest_fs) == 0 || g_str_has_prefix(src_fs, "trash"))) {
// src and dest are on the same filesystem
auto destPath = destDirPath.child(destFileName);
ret = moveFileSameFs(srcPath, srcInfo, destPath);
// increase current progress
// FIXME: it's not appropriate to calculate the progress of move operations using file size
// since the time required to move a file is not related to it's file size.
auto size = g_file_info_get_size(srcInfo.get());
addFinishedAmount(size, 1);
}
else {
// cross device/filesystem move: copy & delete
ret = copyFile(srcPath, srcInfo, destDirPath, destFileName);
// NOTE: do not need to increase progress here since it's done by copyPath().
}
return ret;
}
bool FileTransferJob::copyFile(const FilePath& srcPath, const GFileInfoPtr& srcInfo, const FilePath& destDirPath, const char* destFileName, bool skip) {
setCurrentFile(srcPath);
auto size = g_file_info_get_size(srcInfo.get());
bool success = false;
setCurrentFileProgress(size, 0);
auto destPath = destDirPath.child(destFileName);
auto file_type = g_file_info_get_file_type(srcInfo.get());
if(!skip) {
switch(file_type) {
case G_FILE_TYPE_DIRECTORY:
success = makeDir(srcPath, srcInfo, destPath);
break;
case G_FILE_TYPE_SPECIAL:
success = copySpecialFile(srcPath, srcInfo, destPath);
break;
default:
success = copyRegularFile(srcPath, srcInfo, destPath);
break;
}
}
else { // skip the file
success = true;
}
if(success) {
// finish copying the file
addFinishedAmount(size, 1);
setCurrentFileProgress(0, 0);
// recursively copy dir content
if(file_type == G_FILE_TYPE_DIRECTORY) {
success = copyDirContent(srcPath, srcInfo, destPath, skip);
}
if(!skip && success && mode_ == Mode::MOVE) {
// delete the source file for cross-filesystem move
GErrorPtr err;
if(g_file_delete(srcPath.gfile().get(), cancellable().get(), &err)) {
// FIXME: add some file size to represent the amount of work need to delete a file
addFinishedAmount(1, 1);
}
else {
success = false;
}
}
}
return success;
}
bool FileTransferJob::linkFile(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName) {
setCurrentFile(srcPath);
bool ret = false;
// cannot create links on non-native filesystems
if(!destDirPath.isNative()) {
auto msg = tr("Cannot create a link on non-native filesystem");
GErrorPtr err{g_error_new_literal(G_IO_ERROR, G_IO_ERROR_FAILED, msg.toUtf8().constData())};
emitError(err, ErrorSeverity::CRITICAL);
return false;
}
if(srcPath.isNative()) {
// create symlinks for native files
auto destPath = destDirPath.child(destFileName);
ret = createSymlink(srcPath, srcInfo, destPath);
}
else {
// ensure that the dest file has *.desktop filename extension.
CStrPtr desktopEntryFileName{g_strconcat(destFileName, ".desktop", nullptr)};
auto destPath = destDirPath.child(desktopEntryFileName.get());
ret = createShortcut(srcPath, srcInfo, destPath);
}
// update progress
// FIXME: increase the progress by 1 byte is not appropriate
addFinishedAmount(1, 1);
return ret;
}
bool FileTransferJob::createSymlink(const FilePath &srcPath, const GFileInfoPtr &srcInfo, FilePath &destPath) {
bool ret = false;
auto src = srcPath.localPath();
int flags = 0;
GErrorPtr err;
bool retry;
do {
retry = false;
if(flags & G_FILE_COPY_OVERWRITE) { // overwrite existing file
// creating symlink cannot overwrite existing files directly, so we delete the existing file first.
g_file_delete(destPath.gfile().get(), cancellable().get(), nullptr);
}
if(!g_file_make_symbolic_link(destPath.gfile().get(), src.get(), cancellable().get(), &err)) {
retry = handleError(err, srcPath, srcInfo, destPath, flags);
}
else {
ret = true;
break;
}
} while(!isCancelled() && retry);
return ret;
}
bool FileTransferJob::createShortcut(const FilePath &srcPath, const GFileInfoPtr &srcInfo, FilePath &destPath) {
bool ret = false;
const char* iconName = nullptr;
GIcon* icon = g_file_info_get_icon(srcInfo.get());
if(icon && G_IS_THEMED_ICON(icon)) {
auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(icon));
if(iconNames && iconNames[0]) {
iconName = iconNames[0];
}
}
CStrPtr srcPathUri;
auto uri = g_file_info_get_attribute_string(srcInfo.get(), G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
if(!uri) {
srcPathUri = srcPath.uri();
uri = srcPathUri.get();
}
CStrPtr srcPathDispName;
auto name = g_file_info_get_display_name(srcInfo.get());
if(!name) {
srcPathDispName = srcPath.displayName();
name = srcPathDispName.get();
}
GKeyFile* kf = g_key_file_new();
if(kf) {
g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TYPE, "Link");
g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, name);
if(iconName) {
g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, iconName);
}
if(uri) {
g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_URL, uri);
}
gsize contentLen;
CStrPtr content{g_key_file_to_data(kf, &contentLen, nullptr)};
g_key_file_free(kf);
int flags = 0;
if(content) {
bool retry;
GErrorPtr err;
do {
retry = false;
if(flags & G_FILE_COPY_OVERWRITE) { // overwrite existing file
g_file_delete(destPath.gfile().get(), cancellable().get(), nullptr);
}
if(!g_file_replace_contents(destPath.gfile().get(), content.get(), contentLen, nullptr, false, G_FILE_CREATE_NONE, nullptr, cancellable().get(), &err)) {
retry = handleError(err, srcPath, srcInfo, destPath, flags);
err.reset();
}
else {
ret = true;
}
} while(!isCancelled() && retry);
ret = true;
}
}
return ret;
}
void FileTransferJob::exec() {
// calculate the total size of files to copy
auto totalSizeFlags = (mode_ == Mode::COPY ? TotalSizeJob::DEFAULT : TotalSizeJob::PREPARE_MOVE);
TotalSizeJob totalSizeJob{srcPaths_, totalSizeFlags};
connect(&totalSizeJob, &TotalSizeJob::error, this, &FileTransferJob::error);
connect(this, &FileTransferJob::cancelled, &totalSizeJob, &TotalSizeJob::cancel);
totalSizeJob.run();
if(isCancelled()) {
return;
}
// ready to start
setTotalAmount(totalSizeJob.totalSize(), totalSizeJob.fileCount());
Q_EMIT preparedToRun();
if(srcPaths_.size() != destPaths_.size()) {
qWarning("error: srcPaths.size() != destPaths.size() when copying files");
return;
}
// copy the files
for(size_t i = 0; i < srcPaths_.size(); ++i) {
if(isCancelled()) {
break;
}
const auto& srcPath = srcPaths_[i];
const auto& destPath = destPaths_[i];
auto destDirPath = destPath.parent();
processPath(srcPath, destDirPath, destPath.baseName().get());
}
}
} // namespace Fm

@ -0,0 +1,58 @@
#ifndef FM2_COPYJOB_H
#define FM2_COPYJOB_H
#include "../libfmqtglobals.h"
#include "fileoperationjob.h"
#include "gioptrs.h"
namespace Fm {
class LIBFM_QT_API FileTransferJob : public Fm::FileOperationJob {
Q_OBJECT
public:
enum class Mode {
COPY,
MOVE,
LINK
};
explicit FileTransferJob(FilePathList srcPaths, Mode mode = Mode::COPY);
explicit FileTransferJob(FilePathList srcPaths, FilePathList destPaths, Mode mode = Mode::COPY);
explicit FileTransferJob(FilePathList srcPaths, const FilePath &destDirPath, Mode mode = Mode::COPY);
void setSrcPaths(FilePathList srcPaths);
void setDestPaths(FilePathList destPaths);
void setDestDirPath(const FilePath &destDirPath);
protected:
void exec() override;
private:
bool processPath(const FilePath& srcPath, const FilePath& destPath, const char *destFileName);
bool moveFile(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName);
bool copyFile(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName, bool skip = false);
bool linkFile(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName);
bool moveFileSameFs(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath &destPath);
bool copyRegularFile(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath &destPath);
bool copySpecialFile(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath& destPath);
bool copyDirContent(const FilePath &srcPath, GFileInfoPtr srcInfo, FilePath &destPath, bool skip = false);
bool makeDir(const FilePath &srcPath, GFileInfoPtr srcInfo, FilePath &destPath);
bool createSymlink(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath& destPath);
bool createShortcut(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath& destPath);
bool handleError(GErrorPtr& err, const FilePath &srcPath, const GFileInfoPtr &srcInfo, FilePath &destPath, int& flags);
static void gfileCopyProgressCallback(goffset current_num_bytes, goffset total_num_bytes, FileTransferJob* _this);
private:
FilePathList srcPaths_;
FilePathList destPaths_;
Mode mode_;
};
} // namespace Fm
#endif // FM2_COPYJOB_H

@ -34,8 +34,8 @@
namespace Fm {
std::unordered_map<FilePath, std::weak_ptr<Folder>, FilePathHash> Folder::cache_;
FilePath Folder::cutFilesDirPath_;
FilePath Folder::lastCutFilesDirPath_;
QString Folder::cutFilesDirPath_;
QString Folder::lastCutFilesDirPath_;
std::shared_ptr<const HashSet> Folder::cutFilesHashSet_;
std::mutex Folder::mutex_;
@ -205,9 +205,11 @@ void Folder::onFileInfoFinished() {
return;
FileInfoList files_to_add;
FileInfoList files_to_delete;
std::vector<FileInfoPair> files_to_update;
const auto& paths = job->paths();
const auto& deletionPaths = job->deletionPaths();
const auto& infos = job->files();
auto path_it = paths.cbegin();
auto info_it = infos.cbegin();
@ -218,7 +220,8 @@ void Folder::onFileInfoFinished() {
if(path == dirPath_) { // got the info for the folder itself.
dirInfo_ = info;
}
else {
// add/update the file only if it isn't going to be deleted
else if(std::find(deletionPaths.cbegin(), deletionPaths.cend(), path) == deletionPaths.cend()) {
auto it = files_.find(info->name());
if(it != files_.end()) { // the file already exists, update
files_to_update.push_back(std::make_pair(it->second, info));
@ -235,6 +238,18 @@ void Folder::onFileInfoFinished() {
if(!files_to_update.empty()) {
Q_EMIT filesChanged(files_to_update);
}
// deletion should be done now, after the info job is processed
for(const auto &path: deletionPaths) {
auto name = path.baseName();
auto it = files_.find(name.get());
if(it != files_.end()) {
files_to_delete.push_back(it->second);
files_.erase(it);
}
}
if(!files_to_delete.empty()) {
Q_EMIT filesRemoved(files_to_delete);
}
Q_EMIT contentChanged();
}
@ -250,14 +265,16 @@ void Folder::processPendingChanges() {
}
FileInfoJob* info_job = nullptr;
if(!paths_to_update.empty() || !paths_to_add.empty()) {
FilePathList paths;
if(!paths_to_update.empty() || !paths_to_add.empty() || !paths_to_del.empty()) {
FilePathList paths, deletionPaths;
paths.insert(paths.end(), paths_to_add.cbegin(), paths_to_add.cend());
paths.insert(paths.end(), paths_to_update.cbegin(), paths_to_update.cend());
info_job = new FileInfoJob{paths, dirPath_,
hasCutFiles() ? cutFilesHashSet_ : nullptr};
deletionPaths.insert(deletionPaths.end(), paths_to_del.cbegin(), paths_to_del.cend());
info_job = new FileInfoJob{paths, deletionPaths, dirPath_,
hasCutFiles() ? cutFilesHashSet_ : nullptr};
paths_to_update.clear();
paths_to_add.clear();
paths_to_del.clear();
}
if(info_job) {
@ -275,21 +292,6 @@ void Folder::processPendingChanges() {
#endif
}
if(!paths_to_del.empty()) {
FileInfoList deleted_files;
for(const auto &path: paths_to_del) {
auto name = path.baseName();
auto it = files_.find(name.get());
if(it != files_.end()) {
deleted_files.push_back(it->second);
files_.erase(it);
}
}
Q_EMIT filesRemoved(deleted_files);
Q_EMIT contentChanged();
paths_to_del.clear();
}
if(pending_change_notify) {
Q_EMIT changed();
/* update volume info */
@ -346,10 +348,10 @@ bool Folder::eventFileAdded(const FilePath &path) {
bool Folder::eventFileChanged(const FilePath &path) {
bool added;
// G_LOCK(lists);
/* make sure that the file is not already queued for changes or
* it's already queued for addition. */
/* make sure that the file is not already queued for changes, addition or deletion */
if(std::find(paths_to_update.cbegin(), paths_to_update.cend(), path) == paths_to_update.cend()
&& std::find(paths_to_add.cbegin(), paths_to_add.cend(), path) == paths_to_add.cend()) {
&& std::find(paths_to_add.cbegin(), paths_to_add.cend(), path) == paths_to_add.cend()
&& std::find(paths_to_del.cbegin(), paths_to_del.cend(), path) == paths_to_del.cend()) {
/* Since this function is called only when a file already exists, even if that file
isn't included in "files_" yet, it will be soon due to a previous call to queueUpdate().
So, here, we should queue it for changes regardless of what "files_" may contain. */
@ -367,14 +369,19 @@ bool Folder::eventFileChanged(const FilePath &path) {
void Folder::eventFileDeleted(const FilePath& path) {
// qDebug() << "delete " << path.baseName().get();
// G_LOCK(lists);
if(files_.find(path.baseName().get()) != files_.cend()) {
/* Queue the file for deletion only if it is not in the addition queue but
if it is, remove it from that queue instead of queueing it for deletion.
Moreover, as was the case with eventFileChanged(), here too queueing
should be done regardless of what "files_" may contain. */
if(std::find(paths_to_add.cbegin(), paths_to_add.cend(), path) == paths_to_add.cend()) {
if(std::find(paths_to_del.cbegin(), paths_to_del.cend(), path) == paths_to_del.cend()) {
paths_to_del.push_back(path);
}
}
/* if the file is already queued for addition or update, that operation
will be just a waste, therefore cancel it right now */
paths_to_add.erase(std::remove(paths_to_add.begin(), paths_to_add.end(), path), paths_to_add.cend());
else {
paths_to_add.erase(std::remove(paths_to_add.begin(), paths_to_add.end(), path), paths_to_add.cend());
}
/* the update queue should be canceled for a file that is going to be deleted */
paths_to_update.erase(std::remove(paths_to_update.begin(), paths_to_update.end(), path), paths_to_update.cend());
queueUpdate();
// G_UNLOCK(lists);
@ -451,17 +458,16 @@ void Folder::onFileChangeEvents(GFileMonitor* /*monitor*/, GFile* gf, GFile* /*o
eventFileDeleted(path);
break;
default:
return;
break;
}
queueUpdate();
}
}
// checks whether there were cut files here
// and if there were, invalidates this last cut path
bool Folder::hadCutFilesUnset() {
if(lastCutFilesDirPath_ == dirPath_) {
lastCutFilesDirPath_ = FilePath();
if(lastCutFilesDirPath_ == dirPath_.toString().get()) {
lastCutFilesDirPath_ = QString();
return true;
}
return false;
@ -470,14 +476,14 @@ bool Folder::hadCutFilesUnset() {
bool Folder::hasCutFiles() {
return cutFilesHashSet_
&& !cutFilesHashSet_->empty()
&& cutFilesDirPath_ == dirPath_;
&& cutFilesDirPath_ == dirPath_.toString().get();
}
void Folder::setCutFiles(const std::shared_ptr<const HashSet>& cutFilesHashSet) {
if(cutFilesHashSet_ && !cutFilesHashSet_->empty()) {
lastCutFilesDirPath_ = cutFilesDirPath_;
}
cutFilesDirPath_ = dirPath_;
cutFilesDirPath_ = dirPath_.toString().get();
cutFilesHashSet_ = cutFilesHashSet;
}

@ -185,8 +185,8 @@ private:
bool defer_content_test : 1;
static std::unordered_map<FilePath, std::weak_ptr<Folder>, FilePathHash> cache_;
static FilePath cutFilesDirPath_;
static FilePath lastCutFilesDirPath_;
static QString cutFilesDirPath_;
static QString lastCutFilesDirPath_;
static std::shared_ptr<const HashSet> cutFilesHashSet_;
static std::mutex mutex_;
};

@ -5,7 +5,7 @@ namespace Fm {
std::unordered_map<GIcon*, std::shared_ptr<IconInfo>, IconInfo::GIconHash, IconInfo::GIconEqual> IconInfo::cache_;
std::mutex IconInfo::mutex_;
QIcon IconInfo::fallbackQicon_;
QList<QIcon> IconInfo::fallbackQicons_;
static const char* fallbackIconNames[] = {
"unknown",
@ -15,6 +15,15 @@ static const char* fallbackIconNames[] = {
nullptr
};
static QIcon getFirst(const QList<QIcon> & icons)
{
for (const auto & icon : icons) {
if (!icon.isNull())
return icon;
}
return QIcon{};
}
IconInfo::IconInfo(const char* name):
gicon_{g_themed_icon_new(name), false} {
}
@ -50,10 +59,9 @@ std::shared_ptr<const IconInfo> IconInfo::fromGIcon(GIconPtr gicon) {
void IconInfo::updateQIcons() {
std::lock_guard<std::mutex> lock{mutex_};
fallbackQicon_ = QIcon();
for(auto& elem: cache_) {
auto& info = elem.second;
info->internalQicon_ = QIcon();
info->internalQicons_.clear();
}
}
@ -64,7 +72,7 @@ QIcon IconInfo::qicon(const bool& transparent) const {
qicon_ = QIcon(new IconEngine{shared_from_this()});
}
else {
qicon_ = internalQicon_;
qicon_ = getFirst(internalQicons_);
}
}
}
@ -74,24 +82,21 @@ QIcon IconInfo::qicon(const bool& transparent) const {
qiconTransparent_ = QIcon(new IconEngine{shared_from_this(), transparent});
}
else {
qiconTransparent_ = internalQicon_;
qiconTransparent_ = getFirst(internalQicons_);
}
}
}
return !transparent ? qicon_ : qiconTransparent_;
}
QIcon IconInfo::qiconFromNames(const char* const* names) {
const gchar* const* name;
QList<QIcon> IconInfo::qiconsFromNames(const char* const* names) {
QList<QIcon> icons;
// qDebug("names: %p", names);
for(name = names; *name; ++name) {
for(const gchar* const* name = names; *name; ++name) {
// qDebug("icon name=%s", *name);
QIcon qicon = QIcon::fromTheme(*name);
if(!qicon.isNull()) {
return qicon;
}
icons.push_back(QIcon::fromTheme(*name));
}
return QIcon();
return icons;
}
std::forward_list<std::shared_ptr<const IconInfo>> IconInfo::emblems() const {
@ -109,30 +114,34 @@ std::forward_list<std::shared_ptr<const IconInfo>> IconInfo::emblems() const {
}
QIcon IconInfo::internalQicon() const {
if(Q_UNLIKELY(internalQicon_.isNull())) {
QIcon ret_icon;
if(Q_UNLIKELY(internalQicons_.isEmpty())) {
GIcon* gicon = gicon_.get();
if(G_IS_EMBLEMED_ICON(gicon_.get())) {
gicon = g_emblemed_icon_get_icon(G_EMBLEMED_ICON(gicon));
}
if(G_IS_THEMED_ICON(gicon)) {
const gchar* const* names = g_themed_icon_get_names(G_THEMED_ICON(gicon));
internalQicon_ = qiconFromNames(names);
internalQicons_ = qiconsFromNames(names);
}
else if(G_IS_FILE_ICON(gicon)) {
GFile* file = g_file_icon_get_file(G_FILE_ICON(gicon));
CStrPtr fpath{g_file_get_path(file)};
internalQicon_ = QIcon(fpath.get());
internalQicons_.push_back(QIcon(fpath.get()));
}
// fallback to default icon
if(Q_UNLIKELY(internalQicon_.isNull())) {
if(Q_UNLIKELY(fallbackQicon_.isNull())) {
fallbackQicon_ = qiconFromNames(fallbackIconNames);
}
internalQicon_ = fallbackQicon_;
}
ret_icon = getFirst(internalQicons_);
// fallback to default icon
if(Q_UNLIKELY(ret_icon.isNull())) {
if(Q_UNLIKELY(fallbackQicons_.isEmpty())) {
fallbackQicons_ = qiconsFromNames(fallbackIconNames);
}
ret_icon = getFirst(fallbackQicons_);
}
return internalQicon_;
return ret_icon;
}
} // namespace Fm

@ -77,7 +77,7 @@ public:
private:
static QIcon qiconFromNames(const char* const* names);
static QList<QIcon> qiconsFromNames(const char* const* names);
// actual QIcon loaded by QIcon::fromTheme
QIcon internalQicon() const;
@ -98,11 +98,11 @@ private:
GIconPtr gicon_;
mutable QIcon qicon_;
mutable QIcon qiconTransparent_;
mutable QIcon internalQicon_;
mutable QList<QIcon> internalQicons_;
static std::unordered_map<GIcon*, std::shared_ptr<IconInfo>, GIconHash, GIconEqual> cache_;
static std::mutex mutex_;
static QIcon fallbackQicon_;
static QList<QIcon> fallbackQicons_;
};
} // namespace Fm

@ -0,0 +1,130 @@
#include "templates.h"
#include "gioptrs.h"
#include <algorithm>
#include <QDebug>
using namespace std;
namespace Fm {
std::weak_ptr<Templates> Templates::globalInstance_;
TemplateItem::TemplateItem(std::shared_ptr<const FileInfo> file): fileInfo_{file} {
}
FilePath TemplateItem::filePath() const {
auto& target = fileInfo_->target();
if(fileInfo_->isDesktopEntry() && !target.empty()) {
if(target[0] == '/') { // target is an absolute path
return FilePath::fromLocalPath(target.c_str());
}
else { // resolve relative path
return fileInfo_->dirPath().relativePath(target.c_str());
}
}
return fileInfo_->path();
}
Templates::Templates() : QObject() {
auto* data_dirs = g_get_system_data_dirs();
// system-wide template dirs
for(auto data_dir = data_dirs; *data_dir; ++data_dir) {
CStrPtr dir_name{g_build_filename(*data_dir, "templates", nullptr)};
addTemplateDir(dir_name.get());
}
// user-specific template dir
CStrPtr dir_name{g_build_filename(g_get_user_data_dir(), "templates", nullptr)};
addTemplateDir(dir_name.get());
// $XDG_TEMPLATES_DIR (FIXME: this might change at runtime)
const gchar *special_dir = g_get_user_special_dir(G_USER_DIRECTORY_TEMPLATES);
if (special_dir) {
addTemplateDir(special_dir);
}
}
shared_ptr<Templates> Templates::globalInstance() {
auto templates = globalInstance_.lock();
if(!templates) {
templates = make_shared<Templates>();
globalInstance_ = templates;
}
return templates;
}
void Templates::addTemplateDir(const char* dirPathName) {
auto dir_path = FilePath::fromLocalPath(dirPathName);
if(dir_path.isValid()) {
auto folder = Folder::fromPath(dir_path);
connect(folder.get(), &Folder::filesAdded, this, &Templates::onFilesAdded);
connect(folder.get(), &Folder::filesChanged, this, &Templates::onFilesChanged);
connect(folder.get(), &Folder::filesRemoved, this, &Templates::onFilesRemoved);
connect(folder.get(), &Folder::removed, this, &Templates::onTemplateDirRemoved);
templateFolders_.emplace_back(std::move(folder));
}
}
void Templates::onFilesAdded(FileInfoList& addedFiles) {
for(auto& file : addedFiles) {
// FIXME: we do not support subdirs right now (only XFCE supports this)
if(file->isHidden() || file->isDir()) {
continue;
}
items_.emplace_back(std::make_shared<TemplateItem>(file));
// emit a signal for the addition
Q_EMIT itemAdded(items_.back());
}
}
void Templates::onFilesChanged(std::vector<FileInfoPair>& changePairs) {
for(auto& change: changePairs) {
auto& old_file = change.first;
auto& new_file = change.second;
auto it = std::find_if(items_.begin(), items_.end(), [&](const std::shared_ptr<TemplateItem>& item) {
return item->fileInfo() == old_file;
});
if(it != items_.end()) {
// emit a signal for the change
auto old = *it;
*it = std::make_shared<TemplateItem>(new_file);
Q_EMIT itemChanged(old, *it);
}
}
}
void Templates::onFilesRemoved(FileInfoList& removedFiles) {
for(auto& file : removedFiles) {
auto filePath = file->path();
auto it = std::remove_if(items_.begin(), items_.end(), [&](const std::shared_ptr<TemplateItem>& item) {
return item->fileInfo() == file;
});
for(auto removed_it = it; it != items_.end(); ++it) {
// emit a signal for the removal
Q_EMIT itemRemoved(*removed_it);
}
items_.erase(it, items_.end());
}
}
void Templates::onTemplateDirRemoved() {
// the whole template dir is removed
auto folder = static_cast<Folder*>(sender());
if(!folder) {
return;
}
auto dirPath = folder->path();
// remote all files under this dir
auto it = std::remove_if(items_.begin(), items_.end(), [&](const std::shared_ptr<TemplateItem>& item) {
return dirPath.isPrefixOf(item->filePath());
});
for(auto removed_it = it; it != items_.end(); ++it) {
// emit a signal for the removal
Q_EMIT itemRemoved(*removed_it);
}
items_.erase(it, items_.end());
}
} // namespace Fm

@ -0,0 +1,100 @@
#ifndef TEMPLATES_H
#define TEMPLATES_H
#include "../libfmqtglobals.h"
#include <QObject>
#include <memory>
#include <vector>
#include "folder.h"
#include "fileinfo.h"
#include "mimetype.h"
#include "iconinfo.h"
namespace Fm {
class LIBFM_QT_API TemplateItem {
public:
explicit TemplateItem(std::shared_ptr<const FileInfo> fileInfo);
QString displayName() const {
return fileInfo_->displayName();
}
const std::string& name() const {
return fileInfo_->name();
}
std::shared_ptr<const IconInfo> icon() const {
return fileInfo_->icon();
}
std::shared_ptr<const FileInfo> fileInfo() const {
return fileInfo_;
}
std::shared_ptr<const MimeType> mimeType() const {
return fileInfo_->mimeType();
}
FilePath filePath() const;
private:
std::shared_ptr<const FileInfo> fileInfo_;
};
class LIBFM_QT_API Templates : public QObject {
Q_OBJECT
public:
explicit Templates();
// FIXME: the first call to this method will get no templates since dir loading is in progress.
static std::shared_ptr<Templates> globalInstance();
void forEachItem(std::function<void (const std::shared_ptr<const TemplateItem>&)> func) const {
for(const auto& item : items_) {
func(item);
}
}
std::vector<std::shared_ptr<const TemplateItem>> items() const {
std::vector<std::shared_ptr<const TemplateItem>> tmp_items;
for(auto& item: items_) {
tmp_items.emplace_back(item);
}
return tmp_items;
}
bool hasTemplates() const {
return !items_.empty();
}
Q_SIGNALS:
void itemAdded(const std::shared_ptr<const TemplateItem>& item);
void itemChanged(const std::shared_ptr<const TemplateItem>& oldItem, const std::shared_ptr<const TemplateItem>& newItem);
void itemRemoved(const std::shared_ptr<const TemplateItem>& item);
private:
void addTemplateDir(const char* dirPathName);
private Q_SLOTS:
void onFilesAdded(FileInfoList& addedFiles);
void onFilesChanged(std::vector<FileInfoPair>& changePairs);
void onFilesRemoved(FileInfoList& removedFiles);
void onTemplateDirRemoved();
private:
std::vector<std::shared_ptr<TemplateItem>> items_;
std::vector<std::shared_ptr<Folder>> templateFolders_;
static std::weak_ptr<Templates> globalInstance_;
};
} // namespace Fm
#endif // TEMPLATES_H

@ -21,7 +21,7 @@ CStrPtr Thumbnailer::commandForUri(const char* uri, const char* output_file, gui
/* FIXME: how to handle TryExec? */
/* parse the command line and do required substitutions according to:
* http://developer.gnome.org/integration-guide/stable/thumbnailer.html.en
* https://developer.gnome.org/integration-guide/stable/thumbnailer.html.en
*/
GString* cmd_line = g_string_sized_new(1024);
const char* p;
@ -119,16 +119,17 @@ void Thumbnailer::loadAll() {
CStrPtr file_path{g_build_filename(dir_path, "thumbnailers", base_name.c_str(), nullptr)};
if(g_key_file_load_from_file(kf, file_path.get(), G_KEY_FILE_NONE, nullptr)) {
auto thumbnailer = std::make_shared<Thumbnailer>(base_name.c_str(), kf);
char** mime_types = g_key_file_get_string_list(kf, "Thumbnailer Entry", "MimeType", nullptr, nullptr);
if(mime_types && thumbnailer->exec_) {
for(char** name = mime_types; *name; ++name) {
auto mime_type = MimeType::fromName(*name);
if(mime_type) {
thumbnailer->mimeTypes_.push_back(mime_type);
std::const_pointer_cast<MimeType>(mime_type)->addThumbnailer(thumbnailer);
if(thumbnailer->exec_) {
char** mime_types = g_key_file_get_string_list(kf, "Thumbnailer Entry", "MimeType", nullptr, nullptr);
if(mime_types) {
for(char** name = mime_types; *name; ++name) {
auto mime_type = MimeType::fromName(*name);
if(mime_type) {
std::const_pointer_cast<MimeType>(mime_type)->addThumbnailer(thumbnailer);
}
}
g_strfreev(mime_types);
}
g_strfreev(mime_types);
}
allThumbnailers_.push_back(std::move(thumbnailer));
}

@ -26,7 +26,7 @@ private:
CStrPtr id_;
CStrPtr try_exec_; /* FIXME: is this useful? */
CStrPtr exec_;
std::vector<std::shared_ptr<const MimeType>> mimeTypes_;
//std::vector<std::shared_ptr<const MimeType>> mimeTypes_;
static std::mutex mutex_;
static std::vector<std::shared_ptr<Thumbnailer>> allThumbnailers_;

@ -120,7 +120,7 @@ bool ThumbnailJob::isSupportedImageType(const std::shared_ptr<const MimeType>& m
bool ThumbnailJob::isThumbnailOutdated(const std::shared_ptr<const FileInfo>& file, const QImage &thumbnail) const {
QString thumb_mtime = thumbnail.text("Thumb::MTime");
return (thumb_mtime.isEmpty() || thumb_mtime.toInt() != file->mtime());
return (thumb_mtime.isEmpty() || thumb_mtime.toULongLong() != file->mtime());
}
bool ThumbnailJob::readJpegExif(GInputStream *stream, QImage& thumbnail, int& rotate_degrees) {
@ -140,7 +140,7 @@ bool ThumbnailJob::readJpegExif(GInputStream *stream, QImage& thumbnail, int& ro
exif_loader_unref(exif_loader);
if(exif_data) {
/* reference for EXIF orientation tag:
* http://www.impulseadventure.com/photo/exif-orientation.html */
* https://www.impulseadventure.com/photo/exif-orientation.html */
ExifEntry* orient_ent = exif_data_get_entry(exif_data, EXIF_TAG_ORIENTATION);
if(orient_ent) { /* orientation flag found in EXIF */
gushort orient;

@ -61,37 +61,34 @@ _retry_query_info:
/* prepare for moving across different devices */
if(flags_ & PREPARE_MOVE) {
fs_id = g_file_info_get_attribute_string(inf.get(), G_FILE_ATTRIBUTE_ID_FILESYSTEM);
fs_id = g_intern_string(fs_id);
if(g_strcmp0(fs_id, dest_fs_id) != 0) {
if(fs_id && dest_fs_id && (strcmp(fs_id, dest_fs_id) == 0 || g_str_has_prefix(fs_id, "trash"))) {
// same filesystem or move from trash:///
descend = false;
}
else {
/* files on different device requires an additional 'delete' for the source file. */
++totalSize_; /* this is for the additional delete */
++totalOndiskSize_;
++fileCount_;
}
else {
descend = false;
descend = true;
}
}
if(type == G_FILE_TYPE_DIRECTORY) {
#if 0
FmPath* fm_path = fm_path_new_for_gfile(gf);
/* check if we need to decends into the dir. */
/* trash:/// doesn't support deleting files recursively */
if(flags & PREPARE_DELETE && fm_path_is_trash(fm_path) && ! fm_path_is_trash_root(fm_path)) {
/* trash:/// doesn't support deleting files recursively (but we want to descend into trash root "trash:///" */
if(flags_ & PREPARE_DELETE && path.hasUriScheme("trash") && path.baseName()[0] != '/') {
descend = false;
}
else {
/* only descends into files on the same filesystem */
if(flags & FM_DC_JOB_SAME_FS) {
fs_id = g_file_info_get_attribute_string(inf, G_FILE_ATTRIBUTE_ID_FILESYSTEM);
if(flags_ & SAME_FS) {
fs_id = g_file_info_get_attribute_string(inf.get(), G_FILE_ATTRIBUTE_ID_FILESYSTEM);
descend = (g_strcmp0(fs_id, dest_fs_id) == 0);
}
}
fm_path_unref(fm_path);
#endif
inf = nullptr;
inf = nullptr;
if(descend) {
_retry_enum_children:
GErrorPtr err;

@ -2,10 +2,9 @@
namespace Fm {
TrashJob::TrashJob(const FilePathList& paths): paths_{paths} {
}
TrashJob::TrashJob(const FilePathList&& paths): paths_{paths} {
TrashJob::TrashJob(FilePathList paths): paths_{std::move(paths)} {
// calculate progress using finished file counts rather than their sizes
setCalcProgressUsingSize(false);
}
void TrashJob::exec() {
@ -20,33 +19,32 @@ void TrashJob::exec() {
setCurrentFile(path);
for(;;) {
GErrorPtr err;
GFile* gf = path.gfile().get();
GFileInfoPtr inf{
g_file_query_info(gf, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, G_FILE_QUERY_INFO_NONE,
cancellable().get(), &err),
false
};
// TODO: get parent dir of the current path.
// if there is a Fm::Folder object created for it, block the update for the folder temporarily.
bool ret = FALSE;
for(;;) { // retry the i/o operation on errors
auto gf = path.gfile();
bool ret = false;
// FIXME: do not depend on fm_config
if(fm_config->no_usb_trash) {
err.reset();
GMountPtr mnt{g_file_find_enclosing_mount(gf, nullptr, &err), false};
GMountPtr mnt{g_file_find_enclosing_mount(gf.get(), nullptr, nullptr), false};
if(mnt) {
ret = g_mount_can_unmount(mnt.get()); /* TRUE if it's removable media */
if(ret) {
unsupportedFiles_.push_back(path);
break; // don't trash the file
}
}
}
if(!ret) {
err.reset();
ret = g_file_trash(gf, cancellable().get(), &err);
// move the file to trash
GErrorPtr err;
ret = g_file_trash(gf.get(), cancellable().get(), &err);
if(ret) { // trash operation succeeded
break;
}
if(!ret) {
/* if trashing is not supported by the file system */
else { // failed
// if trashing is not supported by the file system
if(err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_NOT_SUPPORTED) {
unsupportedFiles_.push_back(path);
}

@ -10,8 +10,7 @@ namespace Fm {
class LIBFM_QT_API TrashJob : public Fm::FileOperationJob {
Q_OBJECT
public:
explicit TrashJob(const FilePathList& paths);
explicit TrashJob(const FilePathList&& paths);
explicit TrashJob(FilePathList paths);
FilePathList unsupportedFiles() const {
return unsupportedFiles_;

@ -1,132 +1,76 @@
#include "untrashjob.h"
#include "filetransferjob.h"
namespace Fm {
UntrashJob::UntrashJob() {
}
static const char trash_query[] =
G_FILE_ATTRIBUTE_STANDARD_TYPE","
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME","
G_FILE_ATTRIBUTE_STANDARD_NAME","
G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL","
G_FILE_ATTRIBUTE_STANDARD_SIZE","
G_FILE_ATTRIBUTE_UNIX_BLOCKS","
G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE","
G_FILE_ATTRIBUTE_ID_FILESYSTEM","
"trash::orig-path";
bool UntrashJob::ensure_parent_dir(GFile* orig_path) {
GFile* parent = g_file_get_parent(orig_path);
gboolean ret = g_file_query_exists(parent, cancellable().get());
if(!ret) {
GErrorPtr err;
_retry_mkdir:
if(!g_file_make_directory_with_parents(parent, cancellable().get(), &err)) {
if(!isCancelled()) {
ErrorAction act = emitError(err, ErrorSeverity::MODERATE);
err = nullptr;
if(act == ErrorAction::RETRY) {
goto _retry_mkdir;
}
}
}
else {
ret = TRUE;
}
}
g_object_unref(parent);
return ret;
UntrashJob::UntrashJob(FilePathList srcPaths):
srcPaths_{std::move(srcPaths)} {
}
void UntrashJob::exec() {
#if 0
gboolean ret = TRUE;
GList* l;
GError* err = nullptr;
FmJob* fmjob = FM_JOB(job);
job->total = fm_path_list_get_length(job->srcs);
fm_file_ops_job_emit_prepared(job);
l = fm_path_list_peek_head_link(job->srcs);
for(; !fm_job_is_cancelled(fmjob) && l; l = l->next) {
GFile* gf;
GFileInfo* inf;
FmPath* path = FM_PATH(l->data);
if(!fm_path_is_trash(path)) {
continue;
// preparing for the job
FilePathList validSrcPaths;
FilePathList origPaths;
for(auto& srcPath: srcPaths_) {
if(isCancelled()) {
break;
}
gf = fm_path_to_gfile(path);
_retry_get_orig_path:
inf = g_file_query_info(gf, trash_query, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, fm_job_get_cancellable(fmjob), &err);
if(inf) {
const char* orig_path_str = g_file_info_get_attribute_byte_string(inf, "trash::orig-path");
fm_file_ops_job_emit_cur_file(job, g_file_info_get_display_name(inf));
GErrorPtr err;
GFileInfoPtr srcInfo{
g_file_query_info(srcPath.gfile().get(),
"trash::orig-path",
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
cancellable().get(),
&err),
false
};
if(srcInfo) {
const char* orig_path_str = g_file_info_get_attribute_byte_string(srcInfo.get(), "trash::orig-path");
if(orig_path_str) {
/* FIXME: what if orig_path_str is a relative path?
* This is actually allowed by the horrible trash spec. */
GFile* orig_path = fm_file_new_for_commandline_arg(orig_path_str);
FmFolder* src_folder = fm_folder_find_by_path(fm_path_get_parent(path));
FmPath* orig_fm_path = fm_path_new_for_gfile(orig_path);
FmFolder* dst_folder = fm_folder_find_by_path(fm_path_get_parent(orig_fm_path));
fm_path_unref(orig_fm_path);
/* ensure the existence of parent folder. */
if(ensure_parent_dir(fmjob, orig_path)) {
ret = _fm_file_ops_job_move_file(job, gf, inf, orig_path, path, src_folder, dst_folder);
}
if(src_folder) {
g_object_unref(src_folder);
}
if(dst_folder) {
g_object_unref(dst_folder);
}
g_object_unref(orig_path);
validSrcPaths.emplace_back(srcPath);
origPaths.emplace_back(FilePath::fromPathStr(orig_path_str));
}
else {
ErrorAction act;
g_set_error(&err, G_IO_ERROR, G_IO_ERROR_FAILED,
_("Cannot untrash file '%s': original path not known"),
g_file_info_get_display_name(inf));
act = emitError( err, ErrorSeverity::MODERATE);
g_clear_error(&err);
if(act == ErrorAction::ABORT) {
g_object_unref(inf);
g_object_unref(gf);
return FALSE;
}
tr("Cannot untrash file '%s': original path not known").toUtf8().constData(),
g_file_info_get_display_name(srcInfo.get()));
// FIXME: do we need to retry here?
emitError(err, ErrorSeverity::MODERATE);
}
g_object_unref(inf);
}
else {
char* basename = g_file_get_basename(gf);
char* disp = basename ? g_filename_display_name(basename) : nullptr;
g_free(basename);
/* FIXME: translate it */
fm_file_ops_job_emit_cur_file(job, disp ? disp : "(invalid file)");
g_free(disp);
// FIXME: do we need to retry here?
emitError(err);
}
}
// collected original paths of the trashed files
// use the file transfer job to handle the actual file move
FileTransferJob fileTransferJob{std::move(validSrcPaths), std::move(origPaths), FileTransferJob::Mode::MOVE};
// FIXME:
// I'm not sure why specifying Qt::DirectConnection is needed here since the caller & receiver are in the same thread. :-(
// However without this, the signals/slots here will cause deadlocks.
connect(&fileTransferJob, &FileTransferJob::preparedToRun, this, &UntrashJob::preparedToRun, Qt::DirectConnection);
connect(&fileTransferJob, &FileTransferJob::error, this, &UntrashJob::error, Qt::DirectConnection);
connect(&fileTransferJob, &FileTransferJob::fileExists, this, &UntrashJob::fileExists, Qt::DirectConnection);
if(err) {
ErrorAction act = emitError( err, ErrorSeverity::MODERATE);
g_error_free(err);
err = nullptr;
if(act == ErrorAction::RETRY) {
goto _retry_get_orig_path;
// cancel the file transfer subjob if the parent job is cancelled
connect(this, &UntrashJob::cancelled, &fileTransferJob,
[&fileTransferJob]() {
if(!fileTransferJob.isCancelled()) {
fileTransferJob.cancel();
}
else if(act == ErrorAction::ABORT) {
g_object_unref(gf);
return FALSE;
}, Qt::DirectConnection);
// cancel the parent job if the file transfer subjob is cancelled
connect(&fileTransferJob, &FileTransferJob::cancelled, this,
[this]() {
if(!isCancelled()) {
cancel();
}
}
}
g_object_unref(gf);
++job->finished;
fm_file_ops_job_emit_percent(job);
}
#endif
}, Qt::DirectConnection);
fileTransferJob.run();
}
} // namespace Fm

@ -6,15 +6,15 @@
namespace Fm {
class LIBFM_QT_API UntrashJob : public Fm::FileOperationJob {
class LIBFM_QT_API UntrashJob : public FileOperationJob {
public:
explicit UntrashJob();
explicit UntrashJob(FilePathList srcPaths);
protected:
void exec() override;
private:
bool ensure_parent_dir(GFile *orig_path);
FilePathList srcPaths_;
};
} // namespace Fm

@ -19,14 +19,45 @@
#include "createnewmenu.h"
#include "folderview.h"
#include "icontheme.h"
#include "utilities.h"
#include "core/iconinfo.h"
#include "core/templates.h"
#include <algorithm>
namespace Fm {
class TemplateAction: public QAction {
public:
TemplateAction(std::shared_ptr<const TemplateItem> item, QObject* parent):
QAction(parent) {
setTemplateItem(std::move(item));
}
const std::shared_ptr<const TemplateItem> templateItem() const {
return templateItem_;
}
void setTemplateItem(std::shared_ptr<const TemplateItem> item) {
templateItem_ = std::move(item);
auto mimeType = templateItem_->mimeType();
setText(QString("%1 (%2)").arg(templateItem_->displayName()).arg(mimeType->desc()));
setIcon(templateItem_->icon()->qicon());
}
private:
std::shared_ptr<const TemplateItem> templateItem_;
};
CreateNewMenu::CreateNewMenu(QWidget* dialogParent, Fm::FilePath dirPath, QWidget* parent):
QMenu(parent), dialogParent_(dialogParent), dirPath_(std::move(dirPath)) {
QMenu(parent),
dialogParent_(dialogParent),
dirPath_(std::move(dirPath)),
templateSeparator_{nullptr},
templates_{Templates::globalInstance()} {
QAction* action = new QAction(QIcon::fromTheme("folder-new"), tr("Folder"), this);
connect(action, &QAction::triggered, this, &CreateNewMenu::onCreateNewFolder);
addAction(action);
@ -36,27 +67,12 @@ CreateNewMenu::CreateNewMenu(QWidget* dialogParent, Fm::FilePath dirPath, QWidge
addAction(action);
// add more items to "Create New" menu from templates
GList* templates = fm_template_list_all(fm_config->only_user_templates);
if(templates) {
addSeparator();
for(GList* l = templates; l; l = l->next) {
FmTemplate* templ = (FmTemplate*)l->data;
/* we support directories differently */
if(fm_template_is_directory(templ)) {
continue;
}
FmMimeType* mime_type = fm_template_get_mime_type(templ);
const char* label = fm_template_get_label(templ);
QString text = QString("%1 (%2)").arg(QString::fromUtf8(label)).arg(QString::fromUtf8(fm_mime_type_get_desc(mime_type)));
FmIcon* icon = fm_template_get_icon(templ);
if(!icon) {
icon = fm_mime_type_get_icon(mime_type);
}
QAction* action = addAction(Fm::IconInfo::fromGIcon(G_ICON(icon))->qicon(), text);
action->setObjectName(QString::fromUtf8(fm_template_get_name(templ, nullptr)));
connect(action, &QAction::triggered, this, &CreateNewMenu::onCreateNew);
}
}
connect(templates_.get(), &Templates::itemAdded, this, &CreateNewMenu::addTemplateItem);
connect(templates_.get(), &Templates::itemChanged, this, &CreateNewMenu::updateTemplateItem);
connect(templates_.get(), &Templates::itemRemoved, this, &CreateNewMenu::removeTemplateItem);
templates_->forEachItem([this](const std::shared_ptr<const TemplateItem>& item) {
addTemplateItem(item);
});
}
CreateNewMenu::~CreateNewMenu() {
@ -75,22 +91,62 @@ void CreateNewMenu::onCreateNewFolder() {
}
void CreateNewMenu::onCreateNew() {
QAction* action = static_cast<QAction*>(sender());
QByteArray name = action->objectName().toUtf8();
GList* templates = fm_template_list_all(fm_config->only_user_templates);
FmTemplate* templ = nullptr;
for(GList* l = templates; l; l = l->next) {
FmTemplate* t = (FmTemplate*)l->data;
if(name == fm_template_get_name(t, nullptr)) {
templ = t;
TemplateAction* action = static_cast<TemplateAction*>(sender());
if(dirPath_) {
createFileOrFolder(CreateWithTemplate, dirPath_, action->templateItem().get(), dialogParent_);
}
}
void CreateNewMenu::addTemplateItem(const std::shared_ptr<const TemplateItem> &item) {
if(!templateSeparator_) {
templateSeparator_= addSeparator();
}
auto mimeType = item->mimeType();
/* we support directories differently */
if(mimeType->isDir()) {
return;
}
QAction* action = new TemplateAction{item, this};
connect(action, &QAction::triggered, this, &CreateNewMenu::onCreateNew);
addAction(action);
}
void CreateNewMenu::updateTemplateItem(const std::shared_ptr<const TemplateItem> &oldItem, const std::shared_ptr<const TemplateItem> &newItem) {
auto allActions = actions();
auto separatorPos = allActions.indexOf(templateSeparator_);
// all items after the separator are templates
for(auto i = separatorPos + 1; i < allActions.size(); ++i) {
auto action = static_cast<TemplateAction*>(allActions[i]);
if(action->templateItem() == oldItem) {
// update the menu item
action->setTemplateItem(newItem);
break;
}
}
if(templ) { // template found
if(dirPath_) {
createFileOrFolder(CreateWithTemplate, dirPath_, templ, dialogParent_);
}
void CreateNewMenu::removeTemplateItem(const std::shared_ptr<const TemplateItem> &item) {
if(!templateSeparator_) {
return;
}
auto allActions = actions();
auto separatorPos = allActions.indexOf(templateSeparator_);
// all items after the separator are templates
for(auto i = separatorPos + 1; i < allActions.size(); ++i) {
auto action = static_cast<TemplateAction*>(allActions[i]);
if(action->templateItem() == item) {
// delete the action from the menu
removeAction(action);
allActions.removeAt(i);
break;
}
}
// no more template items. remove the separator
if(separatorPos == allActions.size() - 1) {
removeAction(templateSeparator_);
templateSeparator_ = nullptr;
}
}
} // namespace Fm

@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
*
* This library is free software; you can redistribute it and/or
@ -29,6 +29,8 @@
namespace Fm {
class FolderView;
class Templates;
class TemplateItem;
class LIBFM_QT_API CreateNewMenu : public QMenu {
Q_OBJECT
@ -39,12 +41,23 @@ public:
protected Q_SLOTS:
void onCreateNewFolder();
void onCreateNewFile();
void onCreateNew();
private Q_SLOTS:
void addTemplateItem(const std::shared_ptr<const TemplateItem>& item);
void updateTemplateItem(const std::shared_ptr<const TemplateItem>& oldItem, const std::shared_ptr<const TemplateItem>& newItem);
void removeTemplateItem(const std::shared_ptr<const TemplateItem>& item);
private:
QWidget* dialogParent_;
Fm::FilePath dirPath_;
QAction* templateSeparator_;
std::shared_ptr<Templates> templates_;
};
}

@ -373,12 +373,17 @@ bool FileActionCondition::match_folder(const FileInfoList& files, const char* fo
pattern = g_pattern_spec_new(folder);
}
else {
auto pat_str = string(folder) + "/*";
auto pat_str = g_str_has_suffix(folder, "/") ? string(folder) + "*" // be tolerant
: string(folder) + "/*";
pattern = g_pattern_spec_new(pat_str.c_str());
}
for(auto& fi: files) {
auto dirname = fi->dirPath().toString();
if(g_pattern_match_string(pattern, dirname.get())) { // at least 1 file is in the folder
auto dirname = fi->isDir() ? fi->path().toString() // also match "folder" itself
: fi->dirPath().toString();
// Since the pattern ends with "/*", if the directory path is equal to "folder",
// it should end with "/" to be found as a match. Adding "/" is always harmless.
auto path_str = string(dirname.get()) + "/";
if(g_pattern_match_string(pattern, path_str.c_str())) { // at least 1 file is in the folder
if(negated) {
return false;
}

@ -32,6 +32,9 @@ FileActionProfile::FileActionProfile(GKeyFile *kf, const char* profile_name) {
exec_mode = FileActionExecMode::NORMAL;
}
}
else {
exec_mode = FileActionExecMode::NORMAL;
}
startup_notify = g_key_file_get_boolean(kf, group_name.c_str(), "StartupNotify", nullptr);
startup_wm_class = CStrPtr{g_key_file_get_string(kf, group_name.c_str(), "StartupWMClass", nullptr)};

@ -167,7 +167,7 @@ QModelIndex DirTreeModel::indexFromPath(const Fm::FilePath &path) const {
}
DirTreeModelItem* DirTreeModel::itemFromPath(const Fm::FilePath &path) const {
Q_FOREACH(DirTreeModelItem* item, rootItems_) {
for(DirTreeModelItem* const item : qAsConst(rootItems_)) {
if(item->fileInfo_ && path == item->fileInfo_->path()) {
return item;
}
@ -224,7 +224,7 @@ QString DirTreeModel::dispName(const QModelIndex& index) {
void DirTreeModel::setShowHidden(bool show_hidden) {
showHidden_ = show_hidden;
Q_FOREACH(DirTreeModelItem* item, rootItems_) {
for(DirTreeModelItem* const item : qAsConst(rootItems_)) {
item->setShowHidden(show_hidden);
}
}

@ -19,7 +19,6 @@
#include "dirtreemodelitem.h"
#include "dirtreemodel.h"
#include "icontheme.h"
#include <QDebug>
namespace Fm {
@ -55,12 +54,12 @@ DirTreeModelItem::~DirTreeModelItem() {
freeFolder();
// delete child items if needed
if(!children_.empty()) {
Q_FOREACH(DirTreeModelItem* item, children_) {
for(DirTreeModelItem* const item : qAsConst(children_)) {
delete item;
}
}
if(!hiddenChildren_.empty()) {
Q_FOREACH(DirTreeModelItem* item, hiddenChildren_) {
for(DirTreeModelItem* const item : qAsConst(hiddenChildren_)) {
delete item;
}
}
@ -124,7 +123,7 @@ void DirTreeModelItem::unloadFolder() {
// delete all visible child items
model_->beginRemoveRows(index(), 0, children_.size() - 1);
if(!children_.empty()) {
Q_FOREACH(DirTreeModelItem* item, children_) {
for(DirTreeModelItem* const item : qAsConst(children_)) {
delete item;
}
children_.clear();
@ -133,7 +132,7 @@ void DirTreeModelItem::unloadFolder() {
// remove hidden children
if(!hiddenChildren_.empty()) {
Q_FOREACH(DirTreeModelItem* item, hiddenChildren_) {
for(DirTreeModelItem* const item : qAsConst(hiddenChildren_)) {
delete item;
}
hiddenChildren_.clear();
@ -343,7 +342,7 @@ DirTreeModelItem* DirTreeModelItem::childFromName(const char* utf8_name, int* po
DirTreeModelItem* DirTreeModelItem::childFromPath(Fm::FilePath path, bool recursive) const {
Q_ASSERT(path != nullptr);
Q_FOREACH(DirTreeModelItem* item, children_) {
for(DirTreeModelItem* const item : qAsConst(children_)) {
// if(item->fileInfo_)
// qDebug() << "child: " << QString::fromUtf8(fm_file_info_get_disp_name(item->fileInfo_));
if(item->fileInfo_ && item->fileInfo_->path() == path) {

@ -306,7 +306,7 @@ void DirTreeView::rowsRemoved(const QModelIndex& parent, int start, int end) {
void DirTreeView::doQueuedDeletions() {
if(!queuedForDeletion_.empty()) {
Q_FOREACH(DirTreeModelItem* item, queuedForDeletion_) {
for(DirTreeModelItem* const item : qAsConst(queuedForDeletion_)) {
delete item;
}
queuedForDeletion_.clear();

@ -51,6 +51,7 @@ bool DndDest::dropMimeData(const QMimeData* data, Qt::DropAction action) {
break;
case Qt::LinkAction:
FileOperation::symlinkFiles(srcPaths, destPath_);
/* Falls through. */
default:
return false;
}

@ -102,8 +102,8 @@ void EditBookmarksDialog::onAddItem() {
}
void EditBookmarksDialog::onRemoveItem() {
QList<QTreeWidgetItem*> sels = ui->treeWidget->selectedItems();
Q_FOREACH(QTreeWidgetItem* item, sels) {
const QList<QTreeWidgetItem*> sels = ui->treeWidget->selectedItems();
for(QTreeWidgetItem* const item : sels) {
delete item;
}
}

@ -19,31 +19,37 @@
#include "execfiledialog_p.h"
#include "ui_exec-file.h"
#include "icontheme.h"
#include "core/iconinfo.h"
namespace Fm {
ExecFileDialog::ExecFileDialog(FmFileInfo* file, QWidget* parent, Qt::WindowFlags f):
ExecFileDialog::ExecFileDialog(const FileInfo &fileInfo, QWidget* parent, Qt::WindowFlags f):
QDialog(parent, f),
ui(new Ui::ExecFileDialog()),
fileInfo_(fm_file_info_ref(file)),
result_(FM_FILE_LAUNCHER_EXEC_CANCEL) {
result_(BasicFileLauncher::ExecAction::DIRECT_EXEC) {
ui->setupUi(this);
// show file icon
GIcon* gicon = G_ICON(fm_file_info_get_icon(fileInfo_));
ui->icon->setPixmap(Fm::IconInfo::fromGIcon(gicon)->qicon().pixmap(QSize(48, 48)));
auto gicon = fileInfo.icon();
if(gicon) {
ui->icon->setPixmap(gicon->qicon().pixmap(QSize(48, 48)));
}
QString msg;
if(fm_file_info_is_text(file)) {
if(fileInfo.isDesktopEntry()) {
msg = tr("This file '%1' seems to be a desktop entry.\nWhat do you want to do with it?")
.arg(fileInfo.displayName());
ui->exec->setDefault(true);
ui->execTerm->hide();
}
else if(fileInfo.isText()) {
msg = tr("This text file '%1' seems to be an executable script.\nWhat do you want to do with it?")
.arg(QString::fromUtf8(fm_file_info_get_disp_name(file)));
.arg(fileInfo.displayName());
ui->execTerm->setDefault(true);
}
else {
msg = tr("This file '%1' is executable. Do you want to execute it?")
.arg(QString::fromUtf8(fm_file_info_get_disp_name(file)));
.arg(fileInfo.displayName());
ui->exec->setDefault(true);
ui->open->hide();
}
@ -52,23 +58,28 @@ ExecFileDialog::ExecFileDialog(FmFileInfo* file, QWidget* parent, Qt::WindowFlag
ExecFileDialog::~ExecFileDialog() {
delete ui;
if(fileInfo_) {
fm_file_info_unref(fileInfo_);
}
}
void ExecFileDialog::accept() {
QObject* _sender = sender();
if(_sender == ui->exec) {
result_ = FM_FILE_LAUNCHER_EXEC;
result_ = BasicFileLauncher::ExecAction::DIRECT_EXEC;
}
else if(_sender == ui->execTerm) {
result_ = FM_FILE_LAUNCHER_EXEC_IN_TERMINAL;
result_ = BasicFileLauncher::ExecAction::EXEC_IN_TERMINAL;
}
else if(_sender == ui->open) {
result_ = FM_FILE_LAUNCHER_EXEC_OPEN;
result_ = BasicFileLauncher::ExecAction::OPEN_WITH_DEFAULT_APP;
}
else {
result_ = BasicFileLauncher::ExecAction::CANCEL;
}
QDialog::accept();
}
void ExecFileDialog::reject() {
result_ = BasicFileLauncher::ExecAction::CANCEL;
QDialog::reject();
}
} // namespace Fm

@ -20,8 +20,12 @@
#ifndef FM_EXECFILEDIALOG_H
#define FM_EXECFILEDIALOG_H
#include "core/basicfilelauncher.h"
#include "core/fileinfo.h"
#include <QDialog>
#include <libfm/fm.h>
#include <memory>
namespace Ui {
class ExecFileDialog;
@ -33,19 +37,19 @@ class ExecFileDialog : public QDialog {
Q_OBJECT
public:
~ExecFileDialog();
ExecFileDialog(FmFileInfo* fileInfo, QWidget* parent = 0, Qt::WindowFlags f = 0);
ExecFileDialog(const FileInfo& fileInfo, QWidget* parent = 0, Qt::WindowFlags f = 0);
FmFileLauncherExecAction result() {
BasicFileLauncher::ExecAction result() {
return result_;
}
protected:
virtual void accept();
virtual void accept() override;
virtual void reject() override;
private:
Ui::ExecFileDialog* ui;
FmFileInfo* fileInfo_;
FmFileLauncherExecAction result_;
BasicFileLauncher::ExecAction result_;
};
}

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>450</width>
<height>246</height>
<height>297</height>
</rect>
</property>
<property name="windowTitle">
@ -34,7 +34,7 @@
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="dest">
<widget class="Fm::ElidedLabel" name="dest">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -57,7 +57,7 @@
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="curFile">
<widget class="Fm::ElidedLabel" name="curFile">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -122,12 +122,12 @@
<item row="4" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Data transferred:</string>
<string>Files processed:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLabel" name="dataTransferred">
<widget class="QLabel" name="filesProcessed">
<property name="text">
<string/>
</property>
@ -147,6 +147,13 @@
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Fm::ElidedLabel</class>
<extends>QLabel</extends>
<header>fileoperationdialog_p.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>

@ -96,7 +96,7 @@
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -119,7 +119,7 @@
<string/>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -142,7 +142,7 @@
<string/>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -165,7 +165,7 @@
<string/>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -188,7 +188,7 @@
<string/>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -211,7 +211,7 @@
<string/>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -237,7 +237,7 @@
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
@ -246,9 +246,6 @@
<property name="text">
<string>Open With:</string>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
<item row="5" column="1">
@ -274,7 +271,7 @@
<string/>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>

@ -346,41 +346,43 @@ void FileDialog::goHome() {
}
void FileDialog::setDirectoryPath(FilePath directory, FilePath selectedPath, bool addHistory) {
if(!directory.isValid() || directoryPath_ == directory) {
if(!directory.isValid()) {
updateAcceptButtonState(); // FIXME: is this needed?
return;
}
if(folder_) {
if(folderModel_) {
proxyModel_->setSourceModel(nullptr);
folderModel_->unref(); // unref the cached model
folderModel_ = nullptr;
}
freeFolder();
if(directoryPath_ != directory) {
if(folder_) {
if(folderModel_) {
proxyModel_->setSourceModel(nullptr);
folderModel_->unref(); // unref the cached model
folderModel_ = nullptr;
}
freeFolder();
}
directoryPath_ = std::move(directory);
ui->location->setPath(directoryPath_);
ui->sidePane->chdir(directoryPath_);
if(addHistory) {
history_.add(directoryPath_);
}
backAction_->setEnabled(history_.canBackward());
forwardAction_->setEnabled(history_.canForward());
folder_ = Fm::Folder::fromPath(directoryPath_);
folderModel_ = CachedFolderModel::modelFromFolder(folder_);
proxyModel_->setSourceModel(folderModel_);
// no lambda in these connections for easy disconnection
connect(folder_.get(), &Fm::Folder::removed, this, &FileDialog::goHome);
connect(folder_.get(), &Fm::Folder::unmount, this, &FileDialog::goHome);
QUrl uri = QUrl::fromEncoded(directory.uri().get());
Q_EMIT directoryEntered(uri);
}
directoryPath_ = std::move(directory);
ui->location->setPath(directoryPath_);
ui->sidePane->chdir(directoryPath_);
if(addHistory) {
history_.add(directoryPath_);
}
backAction_->setEnabled(history_.canBackward());
forwardAction_->setEnabled(history_.canForward());
folder_ = Fm::Folder::fromPath(directoryPath_);
folderModel_ = CachedFolderModel::modelFromFolder(folder_);
proxyModel_->setSourceModel(folderModel_);
// no lambda in these connections for easy disconnection
connect(folder_.get(), &Fm::Folder::removed, this, &FileDialog::goHome);
connect(folder_.get(), &Fm::Folder::unmount, this, &FileDialog::goHome);
QUrl uri = QUrl::fromEncoded(directory.uri().get());
Q_EMIT directoryEntered(uri);
// select the path if valid
if(selectedPath.isValid()) {
if(folder_->isLoaded()) {
@ -415,13 +417,13 @@ void FileDialog::selectFilePath(const FilePath &path) {
QItemSelectionModel* selModel = ui->folderView->selectionModel();
selModel->select(idx, flags);
selModel->setCurrentIndex(idx, QItemSelectionModel::Current);
QTimer::singleShot(0, [this, idx]() {
QTimer::singleShot(0, this, [this, idx]() {
ui->folderView->childView()->scrollTo(idx, QAbstractItemView::PositionAtCenter);
});
}
void FileDialog::selectFilePathWithDelay(const FilePath &path) {
QTimer::singleShot(0, [this, path]() {
QTimer::singleShot(0, this, [this, path]() {
if(acceptMode_ == QFileDialog::AcceptSave) {
// with a save dialog, always put the base name in line-edit, regardless of selection
ui->fileName->setText(path.baseName().get());
@ -436,7 +438,7 @@ void FileDialog::selectFilePathWithDelay(const FilePath &path) {
void FileDialog::selectFilesOnReload(const Fm::FileInfoList& infos) {
QObject::disconnect(lambdaConnection_);
QTimer::singleShot(0, [this, infos]() {
QTimer::singleShot(0, this, [this, infos]() {
for(auto& fileInfo: infos) {
selectFilePath(fileInfo->path());
}
@ -635,13 +637,8 @@ void FileDialog::selectFile(const QUrl& filename) {
auto urlStr = filename.toEncoded();
auto path = FilePath::fromUri(urlStr.constData());
auto parent = path.parent();
if(parent.isValid() && parent != directoryPath_) {
// chdir into file's parent if it isn't the current directory
setDirectoryPath(parent, path);
}
else {
selectFilePathWithDelay(path);
}
// chdir into file's parent if needed and select the file
setDirectoryPath(parent, path);
}
QList<QUrl> FileDialog::selectedFiles() {
@ -770,7 +767,8 @@ void FileDialog::setMimeTypeFilters(const QStringList& filters) {
auto nameFilter = mimeType.comment();
if(!mimeType.suffixes().empty()) {
nameFilter + " (";
for(const auto& suffix: mimeType.suffixes()) {
const auto suffixes = mimeType.suffixes();
for(const auto& suffix: suffixes) {
nameFilter += "*.";
nameFilter += suffix;
nameFilter += ' ';

@ -26,111 +26,73 @@
#include "appchooserdialog.h"
#include "utilities.h"
#include "core/fileinfojob.h"
#include "mountoperation.h"
namespace Fm {
FmFileLauncher FileLauncher::funcs = {
FileLauncher::_getApp,
/* gboolean (*before_open)(GAppLaunchContext* ctx, GList* folder_infos, gpointer user_data); */
(FmLaunchFolderFunc)FileLauncher::_openFolder,
FileLauncher::_execFile,
FileLauncher::_error,
FileLauncher::_ask,
nullptr
};
FileLauncher::FileLauncher():
quickExec_(false) {
FileLauncher::FileLauncher() {
}
FileLauncher::~FileLauncher() {
}
bool FileLauncher::launchFiles(QWidget *parent, Fm::FileInfoList file_infos) {
// FIXME: rewrite
return launchPaths(parent, file_infos.paths());
}
bool FileLauncher::launchPaths(QWidget *parent, Fm::FilePathList paths) {
// FIXME: rewrite, port to new api
GList* tmp = nullptr;
for(auto& path: paths) {
auto fmpath = fm_path_new_for_gfile(path.gfile().get());
tmp = g_list_prepend(tmp, fmpath);
}
tmp = g_list_reverse(tmp);
bool ret = launchPaths(parent, tmp);
g_list_free(tmp);
bool FileLauncher::launchFiles(QWidget* parent, const FileInfoList &file_infos) {
GObjectPtr<FmAppLaunchContext> context{fm_app_launch_context_new_for_widget(parent), false};
bool ret = BasicFileLauncher::launchFiles(file_infos, G_APP_LAUNCH_CONTEXT(context.get()));
return ret;
}
bool FileLauncher::launchFiles(QWidget* parent, GList* file_infos) {
FmAppLaunchContext* context = fm_app_launch_context_new_for_widget(parent);
bool ret = fm_launch_files(G_APP_LAUNCH_CONTEXT(context), file_infos, &funcs, this);
g_object_unref(context);
bool FileLauncher::launchPaths(QWidget* parent, const FilePathList& paths) {
GObjectPtr<FmAppLaunchContext> context{fm_app_launch_context_new_for_widget(parent), false};
bool ret = BasicFileLauncher::launchPaths(paths, G_APP_LAUNCH_CONTEXT(context.get()));
return ret;
}
bool FileLauncher::launchPaths(QWidget* parent, GList* paths) {
FmAppLaunchContext* context = fm_app_launch_context_new_for_widget(parent);
bool ret = fm_launch_paths(G_APP_LAUNCH_CONTEXT(context), paths, &funcs, this);
g_object_unref(context);
return ret;
int FileLauncher::ask(const char* /*msg*/, char* const* /*btn_labels*/, int /*default_btn*/) {
/* FIXME: set default button properly */
// return fm_askv(data->parent, nullptr, msg, btn_labels);
return -1;
}
GAppInfo* FileLauncher::getApp(GList* /*file_infos*/, FmMimeType* mime_type, GError** /*err*/) {
GAppInfoPtr FileLauncher::chooseApp(const FileInfoList& /*fileInfos*/, const char *mimeType, GErrorPtr& /*err*/) {
AppChooserDialog dlg(nullptr);
if(mime_type) {
dlg.setMimeType(Fm::MimeType::fromName(fm_mime_type_get_type(mime_type)));
GAppInfoPtr app;
if(mimeType) {
dlg.setMimeType(Fm::MimeType::fromName(mimeType));
}
else {
dlg.setCanSetDefault(false);
}
// FIXME: show error properly?
if(execModelessDialog(&dlg) == QDialog::Accepted) {
auto app = dlg.selectedApp();
return app.release();
app = dlg.selectedApp();
}
return nullptr;
return app;
}
bool FileLauncher::openFolder(GAppLaunchContext* /*ctx*/, GList* folder_infos, GError** /*err*/) {
for(GList* l = folder_infos; l; l = l->next) {
FmFileInfo* fi = FM_FILE_INFO(l->data);
qDebug() << " folder:" << QString::fromUtf8(fm_file_info_get_disp_name(fi));
}
return false;
bool FileLauncher::openFolder(GAppLaunchContext *ctx, const FileInfoList &folderInfos, GErrorPtr &err) {
return BasicFileLauncher::openFolder(ctx, folderInfos, err);
}
FmFileLauncherExecAction FileLauncher::execFile(FmFileInfo* file) {
if(quickExec_) {
/* SF bug#838: open terminal for each script may be just a waste.
User should open a terminal and start the script there
in case if user wants to see the script output anyway.
if (fm_file_info_is_text(file))
return FM_FILE_LAUNCHER_EXEC_IN_TERMINAL; */
return FM_FILE_LAUNCHER_EXEC;
}
FmFileLauncherExecAction res = FM_FILE_LAUNCHER_EXEC_CANCEL;
ExecFileDialog dlg(file);
if(execModelessDialog(&dlg) == QDialog::Accepted) {
res = dlg.result();
}
return res;
}
int FileLauncher::ask(const char* /*msg*/, char* const* /*btn_labels*/, int /*default_btn*/) {
/* FIXME: set default button properly */
// return fm_askv(data->parent, nullptr, msg, btn_labels);
return -1;
}
bool FileLauncher::error(GAppLaunchContext* /*ctx*/, GError* err, FmPath* path) {
bool FileLauncher::showError(GAppLaunchContext* /*ctx*/, GErrorPtr &err, const FilePath &path, const FileInfoPtr &info) {
/* ask for mount if trying to launch unmounted path */
if(err->domain == G_IO_ERROR) {
if(path && err->code == G_IO_ERROR_NOT_MOUNTED) {
//if(fm_mount_path(data->parent, path, TRUE))
// return FALSE; /* ask to retry */
MountOperation* op = new MountOperation(true);
op->setAutoDestroy(true);
if(info && info->isMountable()) {
// this is a mountable shortcut (such as computer:///xxxx.drive)
op->mountMountable(path);
}
else {
op->mountEnclosingVolume(path);
}
if(op->wait()) {
// if the mount operation succeeds, we can ignore the error and continue
return true;
}
}
else if(err->code == G_IO_ERROR_FAILED_HANDLED) {
return true; /* don't show error message */
@ -138,7 +100,16 @@ bool FileLauncher::error(GAppLaunchContext* /*ctx*/, GError* err, FmPath* path)
}
QMessageBox dlg(QMessageBox::Critical, QObject::tr("Error"), QString::fromUtf8(err->message), QMessageBox::Ok);
execModelessDialog(&dlg);
return true;
return false;
}
BasicFileLauncher::ExecAction FileLauncher::askExecFile(const FileInfoPtr &file) {
auto res = BasicFileLauncher::ExecAction::CANCEL;
ExecFileDialog dlg(*file);
if(execModelessDialog(&dlg) == QDialog::Accepted) {
res = dlg.result();
}
return res;
}

@ -23,60 +23,31 @@
#include "libfmqtglobals.h"
#include <QWidget>
#include <libfm/fm.h>
#include "core/fileinfo.h"
#include "core/basicfilelauncher.h"
namespace Fm {
class LIBFM_QT_API FileLauncher {
class LIBFM_QT_API FileLauncher: public BasicFileLauncher {
public:
explicit FileLauncher();
virtual ~FileLauncher();
bool launchFiles(QWidget* parent, Fm::FileInfoList file_infos);
bool launchFiles(QWidget* parent, const FileInfoList& file_infos);
bool launchPaths(QWidget* parent, Fm::FilePathList paths);
bool quickExec() const {
return quickExec_;
}
void setQuickExec(bool value) {
quickExec_ = value;
}
bool launchPaths(QWidget* parent, const FilePathList &paths);
protected:
virtual GAppInfo* getApp(GList* file_infos, FmMimeType* mime_type, GError** err);
virtual bool openFolder(GAppLaunchContext* ctx, GList* folder_infos, GError** err);
virtual FmFileLauncherExecAction execFile(FmFileInfo* file);
virtual bool error(GAppLaunchContext* ctx, GError* err, FmPath* path);
virtual int ask(const char* msg, char* const* btn_labels, int default_btn);
GAppInfoPtr chooseApp(const FileInfoList& fileInfos, const char* mimeType, GErrorPtr& err) override;
private:
bool launchFiles(QWidget* parent, GList* file_infos);
bool openFolder(GAppLaunchContext* ctx, const FileInfoList& folderInfos, GErrorPtr& err) override;
bool launchPaths(QWidget* parent, GList* paths);
bool showError(GAppLaunchContext* ctx, GErrorPtr& err, const FilePath& path = FilePath{}, const FileInfoPtr& info = FileInfoPtr{}) override;
static GAppInfo* _getApp(GList* file_infos, FmMimeType* mime_type, gpointer user_data, GError** err) {
return reinterpret_cast<FileLauncher*>(user_data)->getApp(file_infos, mime_type, err);
}
static gboolean _openFolder(GAppLaunchContext* ctx, GList* folder_infos, gpointer user_data, GError** err) {
return reinterpret_cast<FileLauncher*>(user_data)->openFolder(ctx, folder_infos, err);
}
static FmFileLauncherExecAction _execFile(FmFileInfo* file, gpointer user_data) {
return reinterpret_cast<FileLauncher*>(user_data)->execFile(file);
}
static gboolean _error(GAppLaunchContext* ctx, GError* err, FmPath* file, gpointer user_data) {
return reinterpret_cast<FileLauncher*>(user_data)->error(ctx, err, file);
}
static int _ask(const char* msg, char* const* btn_labels, int default_btn, gpointer user_data) {
return reinterpret_cast<FileLauncher*>(user_data)->ask(msg, btn_labels, default_btn);
}
ExecAction askExecFile(const FileInfoPtr& file) override;
private:
static FmFileLauncher funcs;
bool quickExec_; // Don't ask options on launch executable file
int ask(const char* msg, char* const* btn_labels, int default_btn) override;
};
}

@ -20,7 +20,6 @@
#include "filemenu.h"
#include "createnewmenu.h"
#include "icontheme.h"
#include "filepropsdialog.h"
#include "utilities.h"
#include "fileoperation.h"
@ -35,7 +34,7 @@
#include <QDebug>
#include "filemenu_p.h"
#include "core/compat_p.h"
#include "core/archiver.h"
namespace Fm {
@ -207,18 +206,17 @@ FileMenu::FileMenu(Fm::FileInfoList files, std::shared_ptr<const Fm::FileInfo> i
// FIXME: we need to modify upstream libfm to include some Qt-based archiver programs.
if(!allVirtual_) {
if(sameType_) {
// FIXME: port these parts to Fm API
FmArchiver* archiver = fm_archiver_get_default();
auto archiver = Archiver::defaultArchiver();
if(archiver) {
if(fm_archiver_is_mime_type_supported(archiver, mime_type->name())) {
if(archiver->isMimeTypeSupported(mime_type->name())) {
QAction* archiverSeparator = nullptr;
if(cwd_ && archiver->extract_to_cmd) {
if(cwd_ && archiver->canExtractArchivesTo()) {
archiverSeparator = addSeparator();
QAction* action = new QAction(tr("Extract to..."), this);
connect(action, &QAction::triggered, this, &FileMenu::onExtract);
addAction(action);
}
if(archiver->extract_cmd) {
if(archiver->canExtractArchives()) {
if(!archiverSeparator) {
addSeparator();
}
@ -227,7 +225,7 @@ FileMenu::FileMenu(Fm::FileInfoList files, std::shared_ptr<const Fm::FileInfo> i
addAction(action);
}
}
else {
else if(archiver->canCreateArchive()){
addSeparator();
QAction* action = new QAction(tr("Compress"), this);
connect(action, &QAction::triggered, this, &FileMenu::onCompress);
@ -376,7 +374,9 @@ void FileMenu::onRenameTriggered() {
}
}
for(auto& info: files_) {
Fm::renameFile(info, nullptr);
if(!Fm::renameFile(info, nullptr)) {
break;
}
}
}
@ -391,27 +391,23 @@ void FileMenu::setUseTrash(bool trash) {
}
void FileMenu::onCompress() {
FmArchiver* archiver = fm_archiver_get_default();
auto archiver = Archiver::defaultArchiver();
if(archiver) {
auto paths = Fm::_convertPathList(files_.paths());
fm_archiver_create_archive(archiver, nullptr, paths.dataPtr());
archiver->createArchive(nullptr, files_.paths());
}
}
void FileMenu::onExtract() {
FmArchiver* archiver = fm_archiver_get_default();
auto archiver = Archiver::defaultArchiver();
if(archiver) {
auto paths = Fm::_convertPathList(files_.paths());
fm_archiver_extract_archives(archiver, nullptr, paths.dataPtr());
archiver->extractArchives(nullptr, files_.paths());
}
}
void FileMenu::onExtractHere() {
FmArchiver* archiver = fm_archiver_get_default();
auto archiver = Archiver::defaultArchiver();
if(archiver) {
auto paths = Fm::_convertPathList(files_.paths());
auto cwd = Fm::_convertPath(cwd_);
fm_archiver_extract_archives_to(archiver, nullptr, paths.dataPtr(), cwd);
archiver->extractArchivesTo(nullptr, files_.paths(), cwd_);
}
}

@ -20,7 +20,6 @@
#ifndef FM_FILEMENU_P_H
#define FM_FILEMENU_P_H
#include "icontheme.h"
#include <QDebug>
#include "core/gioptrs.h"
#include "core/iconinfo.h"

@ -24,87 +24,207 @@
#include <QElapsedTimer>
#include <QMessageBox>
#include <QDebug>
#include "path.h"
#include "core/compat_p.h"
#include "core/deletejob.h"
#include "core/trashjob.h"
#include "core/untrashjob.h"
#include "core/filetransferjob.h"
#include "core/filechangeattrjob.h"
#include "utilities.h"
namespace Fm {
#define SHOW_DLG_DELAY 1000
FileOperation::FileOperation(Type type, Fm::FilePathList srcFiles, QObject* parent):
FileOperation::FileOperation(Type type, Fm::FilePathList srcPaths, QObject* parent):
QObject(parent),
job_{fm_file_ops_job_new((FmFileOpType)type, Fm::_convertPathList(srcFiles))},
dlg{nullptr},
srcPaths{std::move(srcFiles)},
uiTimer(nullptr),
type_{type},
job_{nullptr},
dlg_{nullptr},
srcPaths_{std::move(srcPaths)},
uiTimer_(nullptr),
elapsedTimer_(nullptr),
lastElapsed_(0),
updateRemainingTime_(true),
autoDestroy_(true) {
g_signal_connect(job_, "ask", G_CALLBACK(onFileOpsJobAsk), this);
g_signal_connect(job_, "ask-rename", G_CALLBACK(onFileOpsJobAskRename), this);
g_signal_connect(job_, "error", G_CALLBACK(onFileOpsJobError), this);
g_signal_connect(job_, "prepared", G_CALLBACK(onFileOpsJobPrepared), this);
g_signal_connect(job_, "cur-file", G_CALLBACK(onFileOpsJobCurFile), this);
g_signal_connect(job_, "percent", G_CALLBACK(onFileOpsJobPercent), this);
g_signal_connect(job_, "finished", G_CALLBACK(onFileOpsJobFinished), this);
g_signal_connect(job_, "cancelled", G_CALLBACK(onFileOpsJobCancelled), this);
switch(type_) {
case Copy:
job_ = new FileTransferJob(srcPaths_, FileTransferJob::Mode::COPY);
break;
case Move:
job_ = new FileTransferJob(srcPaths_, FileTransferJob::Mode::MOVE);
break;
case Link:
job_ = new FileTransferJob(srcPaths_, FileTransferJob::Mode::LINK);
break;
case Delete:
job_ = new Fm::DeleteJob(srcPaths_);
break;
case Trash:
job_ = new Fm::TrashJob(srcPaths_);
break;
case UnTrash:
job_ = new Fm::UntrashJob(srcPaths_);
break;
case ChangeAttr:
job_ = new Fm::FileChangeAttrJob(srcPaths_);
break;
default:
break;
}
if(job_) {
// automatically delete the job object when it's finished.
job_->setAutoDelete(true);
// new C++ jobs
connect(job_, &Fm::Job::finished, this, &Fm::FileOperation::onJobFinish);
connect(job_, &Fm::Job::cancelled, this, &Fm::FileOperation::onJobCancalled);
connect(job_, &Fm::Job::error, this, &Fm::FileOperation::onJobError, Qt::BlockingQueuedConnection);
connect(job_, &Fm::FileOperationJob::fileExists, this, &Fm::FileOperation::onJobFileExists, Qt::BlockingQueuedConnection);
// we block the job deliberately until we prepare to start (initiailize the timer) so we can calculate elapsed time correctly.
connect(job_, &Fm::FileOperationJob::preparedToRun, this, &Fm::FileOperation::onJobPrepared, Qt::BlockingQueuedConnection);
}
}
void FileOperation::disconnectJob() {
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobAsk), this);
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobAskRename), this);
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobError), this);
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobPrepared), this);
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobCurFile), this);
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobPercent), this);
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobFinished), this);
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobCancelled), this);
if(job_) {
disconnect(job_, &Fm::Job::finished, this, &Fm::FileOperation::onJobFinish);
disconnect(job_, &Fm::Job::cancelled, this, &Fm::FileOperation::onJobCancalled);
disconnect(job_, &Fm::Job::error, this, &Fm::FileOperation::onJobError);
disconnect(job_, &Fm::FileOperationJob::fileExists, this, &Fm::FileOperation::onJobFileExists);
disconnect(job_, &Fm::FileOperationJob::preparedToRun, this, &Fm::FileOperation::onJobPrepared);
}
}
FileOperation::~FileOperation() {
if(uiTimer) {
uiTimer->stop();
delete uiTimer;
uiTimer = nullptr;
if(uiTimer_) {
uiTimer_->stop();
delete uiTimer_;
uiTimer_ = nullptr;
}
if(elapsedTimer_) {
delete elapsedTimer_;
elapsedTimer_ = nullptr;
}
}
void FileOperation::setDestination(Fm::FilePath dest) {
destPath_ = std::move(dest);
switch(type_) {
case Copy:
case Move:
case Link:
if(job_) {
static_cast<FileTransferJob*>(job_)->setDestDirPath(destPath_);
}
break;
default:
break;
}
}
void FileOperation::setDestFiles(FilePathList destFiles) {
switch(type_) {
case Copy:
case Move:
case Link:
if(job_) {
static_cast<FileTransferJob*>(job_)->setDestPaths(std::move(destFiles));
}
break;
default:
break;
}
}
void FileOperation::setChmod(mode_t newMode, mode_t newModeMask) {
if(job_) {
disconnectJob();
g_object_unref(job_);
auto job = static_cast<FileChangeAttrJob*>(job_);
job->setFileModeEnabled(true);
job->setFileMode(newMode, newModeMask);
}
}
void FileOperation::setDestination(Fm::FilePath dest) {
destPath = std::move(dest);
auto tmp = Fm::Path::newForGfile(dest.gfile().get());
fm_file_ops_job_set_dest(job_, tmp.dataPtr());
void FileOperation::setChown(uid_t uid, gid_t gid) {
if(job_) {
auto job = static_cast<FileChangeAttrJob*>(job_);
if(uid != INVALID_UID) {
job->setOwnerEnabled(true);
job->setOwner(uid);
}
if(gid != INVALID_GID) {
job->setGroupEnabled(true);
job->setGroup(gid);
}
}
}
void FileOperation::setRecursiveChattr(bool recursive) {
if(job_) {
auto job = static_cast<FileChangeAttrJob*>(job_);
job->setRecursive(recursive);
}
}
bool FileOperation::run() {
delete uiTimer;
delete uiTimer_;
// run the job
uiTimer = new QTimer();
uiTimer->start(SHOW_DLG_DELAY);
connect(uiTimer, &QTimer::timeout, this, &FileOperation::onUiTimeout);
uiTimer_ = new QTimer();
uiTimer_->start(SHOW_DLG_DELAY);
connect(uiTimer_, &QTimer::timeout, this, &FileOperation::onUiTimeout);
if(job_) {
job_->runAsync();
return true;
}
return false;
}
return fm_job_run_async(FM_JOB(job_));
void FileOperation::cancel() {
if(job_) {
job_->cancel();
}
}
void FileOperation::onUiTimeout() {
if(dlg) {
dlg->setCurFile(curFile);
if(dlg_) {
// estimate remaining time based on past history
// FIXME: avoid directly access data member of FmFileOpsJob
if(Q_LIKELY(job_->percent > 0 && updateRemainingTime_)) {
gint64 remaining = elapsedTime() * ((double(100 - job_->percent) / job_->percent) / 1000);
dlg->setRemainingTime(remaining);
if(job_) {
Fm::FilePath curFilePath = job_->currentFile();
// update progress bar
double finishedRatio = job_->progress();
if(finishedRatio > 0.0 && updateRemainingTime_) {
dlg_->setPercent(int(finishedRatio * 100));
std::uint64_t totalSize, totalCount, finishedSize, finishedCount;
job_->totalAmount(totalSize, totalCount);
job_->finishedAmount(finishedSize, finishedCount);
// only show data transferred if the job progress can be calculated by file size.
// for jobs not related to data transfer (for example: change attr, delete,...), hide the UI
if(job_->calcProgressUsingSize()) {
dlg_->setDataTransferred(finishedSize, totalSize);
}
else {
dlg_->setFilesProcessed(finishedCount, totalCount);
}
double remainRatio = 1.0 - finishedRatio;
gint64 remaining = elapsedTime() * (remainRatio / finishedRatio) / 1000;
// qDebug("etime: %llu, finished: %lf, remain:%lf, remaining secs: %llu",
// elapsedTime(), finishedRatio, remainRatio, remaining);
dlg_->setRemainingTime(remaining);
}
// update currently processed file
if(curFilePath_ != curFilePath) {
curFilePath_ = std::move(curFilePath);
// FIXME: make this cleaner
curFile = QString::fromUtf8(curFilePath_.toString().get());
dlg_->setCurFile(curFile);
}
}
// this timeout slot is called every 0.5 second.
// by adding this flag, we can update remaining time every 1 second.
@ -116,125 +236,84 @@ void FileOperation::onUiTimeout() {
}
void FileOperation::showDialog() {
if(!dlg) {
dlg = new FileOperationDialog(this);
dlg->setSourceFiles(srcPaths);
if(!dlg_) {
dlg_ = new FileOperationDialog(this);
dlg_->setSourceFiles(srcPaths_);
if(destPath) {
dlg->setDestPath(destPath);
if(destPath_) {
dlg_->setDestPath(destPath_);
}
if(curFile.isEmpty()) {
dlg->setPrepared();
dlg->setCurFile(curFile);
dlg_->setPrepared();
dlg_->setCurFile(curFile);
}
uiTimer->setInterval(500); // change the interval of the timer
uiTimer_->setInterval(500); // change the interval of the timer
// now the timer is used to update current file display
dlg->show();
dlg_->show();
}
}
gint FileOperation::onFileOpsJobAsk(FmFileOpsJob* /*job*/, const char* question, char* const* options, FileOperation* pThis) {
pThis->pauseElapsedTimer();
pThis->showDialog();
int ret = pThis->dlg->ask(QString::fromUtf8(question), options);
pThis->resumeElapsedTimer();
return ret;
}
gint FileOperation::onFileOpsJobAskRename(FmFileOpsJob* /*job*/, FmFileInfo* src, FmFileInfo* dest, char** new_name, FileOperation* pThis) {
pThis->pauseElapsedTimer();
pThis->showDialog();
QString newName;
int ret = pThis->dlg->askRename(src, dest, newName);
if(!newName.isEmpty()) {
*new_name = g_strdup(newName.toUtf8().constData());
}
pThis->resumeElapsedTimer();
return ret;
void FileOperation::onJobFileExists(const FileInfo& src, const FileInfo& dest, Fm::FileOperationJob::FileExistsAction& response, FilePath& newDest) {
pauseElapsedTimer();
showDialog();
response = dlg_->askRename(src, dest, newDest);
resumeElapsedTimer();
}
void FileOperation::onFileOpsJobCancelled(FmFileOpsJob* /*job*/, FileOperation* /*pThis*/) {
void FileOperation::onJobCancalled() {
qDebug("file operation is cancelled!");
}
void FileOperation::onFileOpsJobCurFile(FmFileOpsJob* /*job*/, const char* cur_file, FileOperation* pThis) {
pThis->curFile = QString::fromUtf8(cur_file);
// We update the current file name in a timeout slot because drawing a string
// in the UI is expansive. Updating the label text too often cause
// significant impact on performance.
// if(pThis->dlg)
// pThis->dlg->setCurFile(pThis->curFile);
}
FmJobErrorAction FileOperation::onFileOpsJobError(FmFileOpsJob* /*job*/, GError* err, FmJobErrorSeverity severity, FileOperation* pThis) {
pThis->pauseElapsedTimer();
pThis->showDialog();
FmJobErrorAction act = pThis->dlg->error(err, severity);
pThis->resumeElapsedTimer();
return act;
void FileOperation::onJobError(const GErrorPtr& err, Fm::Job::ErrorSeverity severity, Fm::Job::ErrorAction& response) {
pauseElapsedTimer();
showDialog();
response = Fm::Job::ErrorAction(dlg_->error(err.get(), severity));
resumeElapsedTimer();
}
void FileOperation::onFileOpsJobFinished(FmFileOpsJob* /*job*/, FileOperation* pThis) {
pThis->handleFinish();
}
void FileOperation::onFileOpsJobPercent(FmFileOpsJob* job, guint percent, FileOperation* pThis) {
if(pThis->dlg) {
pThis->dlg->setPercent(percent);
pThis->dlg->setDataTransferred(job->finished, job->total);
}
}
void FileOperation::onFileOpsJobPrepared(FmFileOpsJob* /*job*/, FileOperation* pThis) {
if(!pThis->elapsedTimer_) {
pThis->elapsedTimer_ = new QElapsedTimer();
pThis->elapsedTimer_->start();
void FileOperation::onJobPrepared() {
if(!elapsedTimer_) {
elapsedTimer_ = new QElapsedTimer();
elapsedTimer_->start();
}
if(pThis->dlg) {
pThis->dlg->setPrepared();
if(dlg_) {
dlg_->setPrepared();
}
}
void FileOperation::handleFinish() {
void FileOperation::onJobFinish() {
disconnectJob();
if(uiTimer) {
uiTimer->stop();
delete uiTimer;
uiTimer = nullptr;
if(uiTimer_) {
uiTimer_->stop();
delete uiTimer_;
uiTimer_ = nullptr;
}
if(dlg) {
dlg->done(QDialog::Accepted);
delete dlg;
dlg = nullptr;
if(dlg_) {
dlg_->done(QDialog::Accepted);
delete dlg_;
dlg_ = nullptr;
}
Q_EMIT finished();
/* sepcial handling for trash
* FIXME: need to refactor this to use a more elegant way later. */
if(job_->type == FM_FILE_OP_TRASH) { /* FIXME: direct access to job struct! */
auto unable_to_trash = static_cast<FmPathList*>(g_object_get_data(G_OBJECT(job_), "trash-unsupported"));
// special handling for trash job
if(type_ == Trash && !job_->isCancelled()) {
auto trashJob = static_cast<Fm::TrashJob*>(job_);
/* some files cannot be trashed because underlying filesystems don't support it. */
if(unable_to_trash) { /* delete them instead */
Fm::FilePathList filesToDel;
for(GList* l = fm_path_list_peek_head_link(unable_to_trash); l; l = l->next) {
filesToDel.push_back(Fm::FilePath{fm_path_to_gfile(FM_PATH(l->data)), false});
}
auto unsupportedFiles = trashJob->unsupportedFiles();
if(!unsupportedFiles.empty()) { /* delete them instead */
/* FIXME: parent window might be already destroyed! */
QWidget* parent = nullptr; // FIXME: currently, parent window is not set
if(QMessageBox::question(parent, tr("Error"),
tr("Some files cannot be moved to trash can because "
"the underlying file systems don't support this operation.\n"
"Do you want to delete them instead?")) == QMessageBox::Yes) {
deleteFiles(std::move(filesToDel), false);
deleteFiles(std::move(unsupportedFiles), false);
}
}
}
g_object_unref(job_);
job_ = nullptr;
if(autoDestroy_) {
delete this;
@ -249,6 +328,15 @@ FileOperation* FileOperation::copyFiles(Fm::FilePathList srcFiles, Fm::FilePath
return op;
}
//static
FileOperation *FileOperation::copyFiles(FilePathList srcFiles, FilePathList destFiles, QWidget *parent) {
qDebug("copy: %s -> %s", srcFiles[0].toString().get(), destFiles[0].toString().get());
FileOperation* op = new FileOperation(FileOperation::Copy, std::move(srcFiles), parent);
op->setDestFiles(std::move(destFiles));
op->run();
return op;
}
// static
FileOperation* FileOperation::moveFiles(Fm::FilePathList srcFiles, Fm::FilePath dest, QWidget* parent) {
FileOperation* op = new FileOperation(FileOperation::Move, std::move(srcFiles), parent);
@ -257,6 +345,14 @@ FileOperation* FileOperation::moveFiles(Fm::FilePathList srcFiles, Fm::FilePath
return op;
}
//static
FileOperation *FileOperation::moveFiles(FilePathList srcFiles, FilePathList destFiles, QWidget *parent) {
FileOperation* op = new FileOperation(FileOperation::Move, std::move(srcFiles), parent);
op->setDestFiles(std::move(destFiles));
op->run();
return op;
}
//static
FileOperation* FileOperation::symlinkFiles(Fm::FilePathList srcFiles, Fm::FilePath dest, QWidget* parent) {
FileOperation* op = new FileOperation(FileOperation::Link, std::move(srcFiles), parent);
@ -265,6 +361,14 @@ FileOperation* FileOperation::symlinkFiles(Fm::FilePathList srcFiles, Fm::FilePa
return op;
}
//static
FileOperation *FileOperation::symlinkFiles(FilePathList srcFiles, FilePathList destFiles, QWidget *parent) {
FileOperation* op = new FileOperation(FileOperation::Link, std::move(srcFiles), parent);
op->setDestFiles(std::move(destFiles));
op->run();
return op;
}
//static
FileOperation* FileOperation::deleteFiles(Fm::FilePathList srcFiles, bool prompt, QWidget* parent) {
if(prompt) {

@ -24,8 +24,8 @@
#include "libfmqtglobals.h"
#include <QObject>
#include <QElapsedTimer>
#include <libfm/fm.h>
#include "core/filepath.h"
#include "core/fileoperationjob.h"
class QTimer;
@ -37,51 +37,47 @@ class LIBFM_QT_API FileOperation : public QObject {
Q_OBJECT
public:
enum Type {
Copy = FM_FILE_OP_COPY,
Move = FM_FILE_OP_MOVE,
Link = FM_FILE_OP_LINK,
Delete = FM_FILE_OP_DELETE,
Trash = FM_FILE_OP_TRASH,
UnTrash = FM_FILE_OP_UNTRASH,
ChangeAttr = FM_FILE_OP_CHANGE_ATTR
Copy,
Move,
Link,
Delete,
Trash,
UnTrash,
ChangeAttr
};
public:
explicit FileOperation(Type type, Fm::FilePathList srcFiles, QObject* parent = 0);
virtual ~FileOperation();
void setDestination(Fm::FilePath dest);
void setChmod(mode_t newMode, mode_t newModeMask) {
fm_file_ops_job_set_chmod(job_, newMode, newModeMask);
}
void setDestFiles(FilePathList destFiles);
void setChown(gint uid, gint gid) {
fm_file_ops_job_set_chown(job_, uid, gid);
}
void setChmod(mode_t newMode, mode_t newModeMask);
void setChown(uid_t uid, gid_t gid);
// This only work for change attr jobs.
void setRecursiveChattr(bool recursive) {
fm_file_ops_job_set_recursive(job_, (gboolean)recursive);
}
void setRecursiveChattr(bool recursive);
bool run();
void cancel() {
if(job_) {
fm_job_cancel(FM_JOB(job_));
}
}
void cancel();
bool isRunning() const {
return job_ ? fm_job_is_running(FM_JOB(job_)) : false;
return job_ && !isCancelled();
}
bool isCancelled() const {
return job_ ? fm_job_is_cancelled(FM_JOB(job_)) : false;
if(job_) {
return job_->isCancelled();
}
return false;
}
FmFileOpsJob* job() {
Fm::FileOperationJob* job() {
return job_;
}
@ -93,32 +89,54 @@ public:
}
Type type() {
return (Type)job_->type;
return type_;
}
// convinient static functions
static FileOperation* copyFiles(Fm::FilePathList srcFiles, Fm::FilePath dest, QWidget* parent = 0);
static FileOperation* copyFiles(Fm::FilePathList srcFiles, Fm::FilePathList destFiles, QWidget* parent = 0);
static FileOperation* copyFile(Fm::FilePath srcFile, Fm::FilePath destFile, QWidget* parent = 0) {
return copyFiles(FilePathList{std::move(srcFile)}, FilePathList{std::move(destFile)}, parent);
}
static FileOperation* moveFiles(Fm::FilePathList srcFiles, Fm::FilePath dest, QWidget* parent = 0);
static FileOperation* moveFiles(Fm::FilePathList srcFiles, Fm::FilePathList destFiles, QWidget* parent = 0);
static FileOperation* moveFile(Fm::FilePath srcFile, Fm::FilePath destFile, QWidget* parent = 0) {
return moveFiles(FilePathList{std::move(srcFile)}, FilePathList{std::move(destFile)}, parent);
}
static FileOperation* symlinkFiles(Fm::FilePathList srcFiles, Fm::FilePath dest, QWidget* parent = 0);
static FileOperation* symlinkFiles(Fm::FilePathList srcFiles, Fm::FilePathList destFiles, QWidget* parent = 0);
static FileOperation* symlinkFile(Fm::FilePath srcFile, Fm::FilePath destFile, QWidget* parent = 0) {
return symlinkFiles(FilePathList{std::move(srcFile)}, FilePathList{std::move(destFile)}, parent);
}
static FileOperation* deleteFiles(Fm::FilePathList srcFiles, bool promp = true, QWidget* parent = 0);
static FileOperation* trashFiles(Fm::FilePathList srcFiles, bool promp = true, QWidget* parent = 0);
static FileOperation* unTrashFiles(Fm::FilePathList srcFiles, QWidget* parent = 0);
static FileOperation* changeAttrFiles(Fm::FilePathList srcFiles, QWidget* parent = 0);
Q_SIGNALS:
void finished();
private Q_SLOTS:
void onJobPrepared();
void onJobFinish();
void onJobCancalled();
void onJobError(const GErrorPtr& err, Fm::Job::ErrorSeverity severity, Fm::Job::ErrorAction& response);
void onJobFileExists(const FileInfo& src, const FileInfo& dest, Fm::FileOperationJob::FileExistsAction& response, FilePath& newDest);
private:
static gint onFileOpsJobAsk(FmFileOpsJob* job, const char* question, char* const* options, FileOperation* pThis);
static gint onFileOpsJobAskRename(FmFileOpsJob* job, FmFileInfo* src, FmFileInfo* dest, char** new_name, FileOperation* pThis);
static FmJobErrorAction onFileOpsJobError(FmFileOpsJob* job, GError* err, FmJobErrorSeverity severity, FileOperation* pThis);
static void onFileOpsJobPrepared(FmFileOpsJob* job, FileOperation* pThis);
static void onFileOpsJobCurFile(FmFileOpsJob* job, const char* cur_file, FileOperation* pThis);
static void onFileOpsJobPercent(FmFileOpsJob* job, guint percent, FileOperation* pThis);
static void onFileOpsJobFinished(FmFileOpsJob* job, FileOperation* pThis);
static void onFileOpsJobCancelled(FmFileOpsJob* job, FileOperation* pThis);
void handleFinish();
void disconnectJob();
void showDialog();
@ -146,11 +164,13 @@ private Q_SLOTS:
void onUiTimeout();
private:
FmFileOpsJob* job_;
FileOperationDialog* dlg;
Fm::FilePath destPath;
Fm::FilePathList srcPaths;
QTimer* uiTimer;
Type type_;
FileOperationJob* job_;
FileOperationDialog* dlg_;
FilePath destPath_;
FilePath curFilePath_;
FilePathList srcPaths_;
QTimer* uiTimer_;
QElapsedTimer* elapsedTimer_;
qint64 lastElapsed_;
bool updateRemainingTime_;

@ -27,6 +27,7 @@
#include "utilities.h"
#include "ui_file-operation-dialog.h"
namespace Fm {
FileOperationDialog::FileOperationDialog(FileOperation* _operation):
@ -41,35 +42,35 @@ FileOperationDialog::FileOperationDialog(FileOperation* _operation):
QString title;
QString message;
switch(_operation->type()) {
case FM_FILE_OP_MOVE:
case FileOperation::Move:
title = tr("Move files");
message = tr("Moving the following files to destination folder:");
break;
case FM_FILE_OP_COPY:
case FileOperation::Copy:
title = tr("Copy Files");
message = tr("Copying the following files to destination folder:");
break;
case FM_FILE_OP_TRASH:
case FileOperation::Trash:
title = tr("Trash Files");
message = tr("Moving the following files to trash can:");
break;
case FM_FILE_OP_DELETE:
case FileOperation::Delete:
title = tr("Delete Files");
message = tr("Deleting the following files:");
ui->dest->hide();
ui->destLabel->hide();
break;
case FM_FILE_OP_LINK:
case FileOperation::Link:
title = tr("Create Symlinks");
message = tr("Creating symlinks for the following files:");
break;
case FM_FILE_OP_CHANGE_ATTR:
case FileOperation::ChangeAttr:
title = tr("Change Attributes");
message = tr("Changing attributes of the following files:");
ui->dest->hide();
ui->destLabel->hide();
break;
case FM_FILE_OP_UNTRASH:
case FileOperation::UnTrash:
title = tr("Restore Trashed Files");
message = tr("Restoring the following files from trash can:");
ui->dest->hide();
@ -100,47 +101,53 @@ int FileOperationDialog::ask(QString /*question*/, char* const* /*options*/) {
return 0;
}
int FileOperationDialog::askRename(FmFileInfo* src, FmFileInfo* dest, QString& new_name) {
int ret;
FileOperationJob::FileExistsAction FileOperationDialog::askRename(const FileInfo &src, const FileInfo &dest, FilePath &newDest) {
FileOperationJob::FileExistsAction ret;
if(defaultOption == -1) { // default action is not set, ask the user
RenameDialog dlg(src, dest, this);
dlg.exec();
switch(dlg.action()) {
case RenameDialog::ActionOverwrite:
ret = FM_FILE_OP_OVERWRITE;
ret = FileOperationJob::OVERWRITE;
if(dlg.applyToAll()) {
defaultOption = ret;
}
break;
case RenameDialog::ActionRename:
ret = FM_FILE_OP_RENAME;
new_name = dlg.newName();
case RenameDialog::ActionRename: {
ret = FileOperationJob::RENAME;
auto newName = dlg.newName();
if(!newName.isEmpty()) {
auto destDirPath = dest.path().parent();
newDest = destDirPath.child(newName.toUtf8().constData());
}
break;
}
case RenameDialog::ActionIgnore:
ret = FM_FILE_OP_SKIP;
ret = FileOperationJob::SKIP;
if(dlg.applyToAll()) {
defaultOption = ret;
}
break;
default:
ret = FM_FILE_OP_CANCEL;
ret = FileOperationJob::CANCEL;
break;
}
}
else {
ret = defaultOption;
ret = (FileOperationJob::FileExistsAction)defaultOption;
}
return ret;
}
FmJobErrorAction FileOperationDialog::error(GError* err, FmJobErrorSeverity severity) {
if(severity >= FM_JOB_ERROR_MODERATE) {
if(severity == FM_JOB_ERROR_CRITICAL) {
Job::ErrorAction FileOperationDialog::error(GError* err, Job::ErrorSeverity severity) {
if(severity >= Job::ErrorSeverity::MODERATE) {
if(severity == Job::ErrorSeverity::CRITICAL) {
QMessageBox::critical(this, tr("Error"), QString::fromUtf8(err->message));
return FM_JOB_ABORT;
return Job::ErrorAction::ABORT;
}
if (ignoreNonCriticalErrors_) {
return FM_JOB_CONTINUE;
return Job::ErrorAction::CONTINUE;
}
QMessageBox::StandardButton stb = QMessageBox::critical(this, tr("Error"), QString::fromUtf8(err->message),
QMessageBox::Ok | QMessageBox::Ignore);
@ -148,7 +155,7 @@ FmJobErrorAction FileOperationDialog::error(GError* err, FmJobErrorSeverity seve
ignoreNonCriticalErrors_ = true;
}
}
return FM_JOB_CONTINUE;
return Job::ErrorAction::CONTINUE;
}
void FileOperationDialog::setCurFile(QString cur_file) {
@ -156,15 +163,22 @@ void FileOperationDialog::setCurFile(QString cur_file) {
}
void FileOperationDialog::setDataTransferred(uint64_t finishedSize, std::uint64_t totalSize) {
ui->dataTransferred->setText(QString("%1 / %2")
ui->filesProcessed->setText(QString("%1 / %2")
.arg(formatFileSize(finishedSize, fm_config->si_unit))
.arg(formatFileSize(totalSize, fm_config->si_unit)));
}
void FileOperationDialog::setFilesProcessed(uint64_t finishedCount, uint64_t totalCount) {
ui->filesProcessed->setText(QString("%1 / %2")
.arg(finishedCount)
.arg(totalCount));
}
void FileOperationDialog::setPercent(unsigned int percent) {
ui->progressBar->setValue(percent);
}
void FileOperationDialog::setRemainingTime(unsigned int sec) {
unsigned int min = 0;
unsigned int hr = 0;

@ -27,6 +27,7 @@
#include <libfm/fm.h>
#include "core/filepath.h"
#include "core/fileinfo.h"
#include "core/fileoperationjob.h"
namespace Ui {
class FileOperationDialog;
@ -46,12 +47,15 @@ public:
void setDestPath(const Fm::FilePath& dest);
int ask(QString question, char* const* options);
int askRename(FmFileInfo* src, FmFileInfo* dest, QString& new_name);
FmJobErrorAction error(GError* err, FmJobErrorSeverity severity);
FileOperationJob::FileExistsAction askRename(const FileInfo& src, const FileInfo& dest, FilePath& newDest);
Job::ErrorAction error(GError* err, Job::ErrorSeverity severity);
void setPrepared();
void setCurFile(QString cur_file);
void setPercent(unsigned int percent);
void setDataTransferred(std::uint64_t finishedSize, std::uint64_t totalSize);
void setFilesProcessed(std::uint64_t finishedCount, std::uint64_t totalCount);
void setRemainingTime(unsigned int sec);
virtual void reject();

@ -0,0 +1,69 @@
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef FM_FILEOPERATIONDIALOG_P_H
#define FM_FILEOPERATIONDIALOG_P_H
#include <QPainter>
#include <QStyleOption>
#include <QLabel>
namespace Fm {
class ElidedLabel : public QLabel {
Q_OBJECT
public:
explicit ElidedLabel(QWidget *parent = 0, Qt::WindowFlags f = Qt::WindowFlags()):
QLabel(parent, f),
lastWidth_(0) {
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
// set a min width to prevent the window from widening with long texts
setMinimumWidth(fontMetrics().averageCharWidth() * 10);
}
protected:
// A simplified version of QLabel::paintEvent() without pixmap or shortcut but with eliding.
void paintEvent(QPaintEvent* /*event*/) override {
QRect cr = contentsRect().adjusted(margin(), margin(), -margin(), -margin());
QString txt = text();
// if the text is changed or its rect is resized (due to window resizing),
// find whether it needs to be elided...
if (txt != lastText_ || cr.width() != lastWidth_) {
lastText_ = txt;
lastWidth_ = cr.width();
elidedText_ = fontMetrics().elidedText(txt, Qt::ElideMiddle, cr.width());
}
// ... then, draw the (elided) text
if(!elidedText_.isEmpty()) {
QPainter painter(this);
QStyleOption opt;
opt.initFrom(this);
style()->drawItemText(&painter, cr, alignment(), opt.palette, isEnabled(), elidedText_, foregroundRole());
}
}
private:
QString elidedText_;
QString lastText_;
int lastWidth_;
};
}
#endif // FM_FILEOPERATIONDIALOG_P_H

@ -20,7 +20,6 @@
#include "filepropsdialog.h"
#include "ui_file-props.h"
#include "icontheme.h"
#include "utilities.h"
#include "fileoperation.h"
#include <QStringBuilder>
@ -403,9 +402,9 @@ void FilePropsDialog::accept() {
}
// check if chown or chmod is needed
gint32 newUid = uidFromName(ui->owner->text());
gint32 newGid = gidFromName(ui->ownerGroup->text());
bool needChown = (newUid != -1 && newUid != uid) || (newGid != -1 && newGid != gid);
uid_t newUid = uidFromName(ui->owner->text());
gid_t newGid = gidFromName(ui->ownerGroup->text());
bool needChown = (newUid != INVALID_UID && newUid != uid) || (newGid != INVALID_GID && newGid != gid);
int newOwnerPermSel = ui->ownerPerm->currentIndex();
int newGroupPermSel = ui->groupPerm->currentIndex();
@ -421,10 +420,10 @@ void FilePropsDialog::accept() {
if(needChown) {
// don't do chown if new uid/gid and the original ones are actually the same.
if(newUid == uid) {
newUid = -1;
newUid = INVALID_UID;
}
if(newGid == gid) {
newGid = -1;
newGid = INVALID_GID;
}
op->setChown(newUid, newGid);
}

@ -80,8 +80,8 @@ private:
std::shared_ptr<const Fm::MimeType> mimeType; // mime type of the files
gint32 uid; // owner uid of the files, -1 means all files do not have the same uid
gint32 gid; // owner gid of the files, -1 means all files do not have the same uid
uid_t uid; // owner uid of the files, INVALID_UID means all files do not have the same uid
gid_t gid; // owner gid of the files, INVALID_GID means all files do not have the same uid
mode_t ownerPerm; // read permission of the files, -1 means not all files have the same value
int ownerPermSel;
mode_t groupPerm; // read permission of the files, -1 means not all files have the same value

@ -33,7 +33,7 @@ FileSearchDialog::FileSearchDialog(QStringList paths, QWidget* parent, Qt::Windo
ui->setupUi(this);
ui->minSize->setMaximum(std::numeric_limits<int>().max());
ui->maxSize->setMaximum(std::numeric_limits<int>().max());
Q_FOREACH(const QString& path, paths) {
for(const QString& path : qAsConst(paths)) {
ui->listView->addItem(path);
}
@ -120,7 +120,7 @@ void FileSearchDialog::accept() {
fm_search_set_min_mtime(search, ui->minTime->date().toString(QStringLiteral("yyyy-MM-dd")).toUtf8().constData());
}
searchUri_ = Path::wrapPtr(fm_search_dup_path(search));
searchUri_ = FilePath{fm_search_to_gfile(search), false};
fm_search_free(search);
}
@ -144,7 +144,8 @@ void FileSearchDialog::onAddPath() {
void FileSearchDialog::onRemovePath() {
// remove selected items
Q_FOREACH(QListWidgetItem* item, ui->listView->selectedItems()) {
const auto itemList = ui->listView->selectedItems();
for(QListWidgetItem* const item : itemList) {
delete item;
}
}

@ -22,7 +22,7 @@
#include "libfmqtglobals.h"
#include <QDialog>
#include "path.h"
#include "core/filepath.h"
namespace Ui {
class SearchDialog;
@ -35,7 +35,7 @@ public:
explicit FileSearchDialog(QStringList paths = QStringList(), QWidget* parent = 0, Qt::WindowFlags f = 0);
~FileSearchDialog();
Path searchUri() const {
const FilePath& searchUri() const {
return searchUri_;
}
@ -65,7 +65,7 @@ private Q_SLOTS:
private:
Ui::SearchDialog* ui;
Path searchUri_;
FilePath searchUri_;
};
}

@ -229,9 +229,9 @@ void fm_search_set_min_mtime(FmSearch* search, const char* mtime)
}
/* really build the path */
FmPath* fm_search_dup_path(FmSearch* search)
GFile* fm_search_to_gfile(FmSearch* search)
{
FmPath* search_path = NULL;
GFile* search_path = NULL;
GString* search_str = g_string_sized_new(1024);
/* build the search:// URI to perform the search */
g_string_append(search_str, "search://");
@ -310,7 +310,7 @@ FmPath* fm_search_dup_path(FmSearch* search)
if(search->max_mtime)
g_string_append_printf(search_str, "&max_mtime=%s", search->max_mtime);
search_path = fm_path_new_for_uri(search_str->str);
search_path = g_file_new_for_uri(search_str->str);
g_string_free(search_str, TRUE);
}
return search_path;

@ -27,7 +27,7 @@
#ifndef _FM_SEARCH_H_
#define _FM_SEARCH_H_
#include <libfm/fm.h>
#include <gio/gio.h>
G_BEGIN_DECLS
@ -36,7 +36,7 @@ typedef struct _FmSearch FmSearch;
FmSearch* fm_search_new(void);
void fm_search_free(FmSearch* search);
FmPath* fm_search_dup_path(FmSearch* search);
GFile* fm_search_to_gfile(FmSearch* search);
gboolean fm_search_get_recursive(FmSearch* search);
void fm_search_set_recursive(FmSearch* search, gboolean recursive);

@ -111,6 +111,16 @@ void FolderItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& op
auto file = index.data(fileInfoRole_).value<std::shared_ptr<const Fm::FileInfo>>();
const auto& emblems = file ? file->emblems() : icon_emblems;
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
// distinguish the hidden items visually by making their texts italic
if(file && file->isHidden()) {
QFont f(opt.font);
f.setItalic(true);
opt.font = f;
}
bool isSymlink = file && file->isSymlink();
// vertical layout (icon mode, thumbnail mode)
if(option.decorationPosition == QStyleOptionViewItem::Top ||
@ -118,8 +128,6 @@ void FolderItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& op
painter->save();
painter->setClipRect(option.rect);
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
opt.decorationAlignment = Qt::AlignHCenter | Qt::AlignTop;
opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter;
@ -168,12 +176,10 @@ void FolderItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& op
// let QStyledItemDelegate does its default painting
// FIXME: For better text alignment, here we should increase
// the icon width if it's smaller that the requested size
QStyledItemDelegate::paint(painter, option, index);
QStyledItemDelegate::paint(painter, opt, index);
// draw emblems if needed
if(isSymlink || !emblems.empty()) {
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
QIcon::Mode iconMode = iconModeFromState(opt.state);
// draw some emblems for the item if needed
if(isSymlink) {
@ -249,7 +255,8 @@ void FolderItemDelegate::drawText(QPainter* painter, QStyleOptionViewItem& opt,
return;
}
// ?????
// Respect the active and inactive palettes (some styles can use different colors for them).
// Also, take into account a probable disabled palette.
QPalette::ColorGroup cg = (opt.state & QStyle::State_Enabled)
? (opt.state & QStyle::State_Active)
? QPalette::Active
@ -350,8 +357,13 @@ QWidget* FolderItemDelegate::createEditor(QWidget* parent, const QStyleOptionVie
return textEdit;
}
else {
// return the default line-edit in compact view
return QStyledItemDelegate::createEditor(parent, option, index);
// return the default line-edit in other views and
// ensure that its background isn't transparent (on the side-pane)
QWidget* editor = QStyledItemDelegate::createEditor(parent, option, index);
QPalette p = editor->palette();
p.setColor(QPalette::Base, QApplication::palette().color(QPalette::Base));
editor->setPalette(p);
return editor;
}
}

@ -19,7 +19,6 @@
#include "foldermodel.h"
#include "icontheme.h"
#include <iostream>
#include <algorithm>
#include <QtAlgorithms>
@ -40,7 +39,6 @@ FolderModel::FolderModel():
}
FolderModel::~FolderModel() {
qDebug("delete FolderModel");
// if the thumbnail requests list is not empty, cancel them
for(auto job: pendingThumbnailJobs_) {
job->cancel();
@ -87,6 +85,7 @@ void FolderModel::onFilesAdded(const Fm::FileInfoList& files) {
items.append(item);
}
endInsertRows();
Q_EMIT filesAdded(files);
}
void FolderModel::onFilesChanged(std::vector<Fm::FileInfoPair>& files) {
@ -161,7 +160,8 @@ void FolderModel::setCutFiles(const QItemSelection& selection) {
if(!selection.isEmpty()) {
auto cutFilesHashSet = std::make_shared<HashSet>();
folder_->setCutFiles(cutFilesHashSet);
for(const auto& index : selection.indexes()) {
const auto indexes = selection.indexes();
for(const auto& index : indexes) {
auto item = itemFromIndex(index);
item->bindCutFiles(cutFilesHashSet);
cutFilesHashSet->insert(item->info->path().hash());
@ -230,6 +230,8 @@ QVariant FolderModel::data(const QModelIndex& index, int role/* = Qt::DisplayRol
return item->displaySize();
case ColumnFileOwner:
return item->ownerName();
case ColumnFileGroup:
return item->ownerGroup();
}
break;
}
@ -275,6 +277,9 @@ QVariant FolderModel::headerData(int section, Qt::Orientation orientation, int r
case ColumnFileOwner:
title = tr("Owner");
break;
case ColumnFileGroup:
title = tr("Group");
break;
}
return QVariant(title);
}
@ -361,12 +366,12 @@ QList< FolderModelItem >::iterator FolderModel::findItemByFileInfo(const Fm::Fil
}
QStringList FolderModel::mimeTypes() const {
qDebug("FolderModel::mimeTypes");
//qDebug("FolderModel::mimeTypes");
QStringList types = QAbstractItemModel::mimeTypes();
// now types contains "application/x-qabstractitemmodeldatalist"
// add support for freedesktop Xdnd direct save (XDS) protocol.
// http://www.freedesktop.org/wiki/Specifications/XDS/#index4h2
// https://www.freedesktop.org/wiki/Specifications/XDS/#index4h2
// the real implementation is in FolderView::childDropEvent().
types << "XdndDirectSave0";
types << "text/uri-list";
@ -376,7 +381,7 @@ QStringList FolderModel::mimeTypes() const {
QMimeData* FolderModel::mimeData(const QModelIndexList& indexes) const {
QMimeData* data = QAbstractItemModel::mimeData(indexes);
qDebug("FolderModel::mimeData");
//qDebug("FolderModel::mimeData");
// build a uri list
QByteArray urilist;
urilist.reserve(4096);
@ -398,7 +403,7 @@ QMimeData* FolderModel::mimeData(const QModelIndexList& indexes) const {
}
bool FolderModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) {
qDebug("FolderModel::dropMimeData");
//qDebug("FolderModel::dropMimeData");
if(!folder_ || !data) {
return false;
}
@ -413,7 +418,12 @@ bool FolderModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int
info = fileInfoFromIndex(itemIndex);
}
if(info) {
destPath = info->path();
if (info->isDir()) {
destPath = info->path();
}
else {
destPath = path(); // don't drop on file
}
}
else {
return false;
@ -425,7 +435,7 @@ bool FolderModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int
// FIXME: should we put this in dropEvent handler of FolderView instead?
if(data->hasUrls()) {
qDebug("drop action: %d", action);
//qDebug("drop action: %d", action);
auto srcPaths = pathListFromQUrls(data->urls());
switch(action) {
case Qt::CopyAction:
@ -436,6 +446,7 @@ bool FolderModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int
break;
case Qt::LinkAction:
FileOperation::symlinkFiles(srcPaths, destPath);
/* Falls through. */
default:
return false;
}
@ -448,7 +459,7 @@ bool FolderModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int
}
Qt::DropActions FolderModel::supportedDropActions() const {
qDebug("FolderModel::supportedDropActions");
//qDebug("FolderModel::supportedDropActions");
return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction;
}

@ -54,6 +54,7 @@ public:
ColumnFileSize,
ColumnFileMTime,
ColumnFileOwner,
ColumnFileGroup,
NumOfColumns
};
@ -98,6 +99,7 @@ public:
Q_SIGNALS:
void thumbnailLoaded(const QModelIndex& index, int size);
void fileSizeChanged(const QModelIndex& index);
void filesAdded(FileInfoList infoList);
protected Q_SLOTS:

@ -43,10 +43,7 @@ QString FolderModelItem::ownerName() const {
QString name;
auto user = Fm::UserInfoCache::globalInstance()->userFromId(info->uid());
if(user) {
name = user->realName();
if(name.isEmpty()) {
name = user->name();
}
name = user->name();
}
return name;
}

@ -27,7 +27,6 @@
#include <QString>
#include <QIcon>
#include <QVector>
#include "icontheme.h"
#include "core/folder.h"

@ -45,12 +45,16 @@
#include <QX11Info> // for XDS support
#include <xcb/xcb.h> // for XDS support
#include "xdndworkaround.h" // for XDS support
#include "path.h"
#include "folderview_p.h"
#include "utilities.h"
Q_DECLARE_OPAQUE_POINTER(FmFileInfo*)
#define SCROLL_FRAMES_PER_SEC 50
#define SCROLL_DURATION 300 // in ms
static const int scrollAnimFrames = SCROLL_FRAMES_PER_SEC * SCROLL_DURATION / 1000;
using namespace Fm;
FolderViewListView::FolderViewListView(QWidget* parent):
@ -144,7 +148,7 @@ void FolderViewListView::dragEnterEvent(QDragEnterEvent* event) {
else {
QAbstractItemView::dragEnterEvent(event);
}
qDebug("dragEnterEvent");
//qDebug("dragEnterEvent");
//static_cast<FolderView*>(parent())->childDragEnterEvent(event);
}
@ -410,7 +414,7 @@ void FolderViewTreeView::reset() {
// This is for performance reason so in this case rowsInserted() and rowsAboutToBeRemoved()
// might not be called. Hence we also have to re-layout the columns when the model is reset.
// This fixes bug #190
// https://github.com/lxde/pcmanfm-qt/issues/190
// https://github.com/lxqt/pcmanfm-qt/issues/190
QTreeView::reset();
queueLayoutColumns();
}
@ -476,7 +480,9 @@ FolderView::FolderView(FolderView::ViewMode _mode, QWidget *parent):
autoSelectionDelay_(600),
autoSelectionTimer_(nullptr),
selChangedTimer_(nullptr),
itemDelegateMargins_(QSize(3, 3)) {
itemDelegateMargins_(QSize(3, 3)),
smoothScrollTimer_(nullptr),
wheelEvent_(nullptr) {
iconSize_[IconMode - FirstViewMode] = QSize(48, 48);
iconSize_[CompactMode - FirstViewMode] = QSize(24, 24);
@ -495,6 +501,11 @@ FolderView::FolderView(FolderView::ViewMode _mode, QWidget *parent):
}
FolderView::~FolderView() {
if(smoothScrollTimer_) {
disconnect(smoothScrollTimer_, &QTimer::timeout, this, &FolderView::scrollSmoothly);
smoothScrollTimer_->stop();
delete smoothScrollTimer_;
}
}
void FolderView::onItemActivated(QModelIndex index) {
@ -567,6 +578,13 @@ void FolderView::setViewMode(ViewMode _mode) {
if(_mode == mode) { // if it's the same more, ignore
return;
}
// smooth scrolling is only for icon and thumbnail modes
if(smoothScrollTimer_ && (_mode == DetailedListMode || _mode == CompactMode)) {
disconnect(smoothScrollTimer_, &QTimer::timeout, this, &FolderView::scrollSmoothly);
smoothScrollTimer_->stop();
delete smoothScrollTimer_;
smoothScrollTimer_ = nullptr;
}
// FIXME: retain old selection
// since only detailed list mode uses QTreeView, and others
@ -656,11 +674,10 @@ void FolderView::setViewMode(ViewMode _mode) {
view->setSelectionMode(QAbstractItemView::ExtendedSelection);
layout()->addWidget(view);
// enable dnd
// enable dnd (the drop indicator is set at "FolderView::childDragMoveEvent()")
view->setDragEnabled(true);
view->setAcceptDrops(true);
view->setDragDropMode(QAbstractItemView::DragDrop);
view->setDropIndicatorShown(true);
// inline renaming
if(delegate) {
@ -891,6 +908,43 @@ QModelIndex FolderView::indexFromFolderPath(const Fm::FilePath& folderPath) cons
return QModelIndex();
}
void FolderView::selectFiles(const Fm::FileInfoList& files, bool add) {
if(!model_ || files.empty()) {
return;
}
if(!add) {
selectionModel()->clear();
}
QModelIndex index, firstIndex;
int count = model_->rowCount();
Fm::FileInfoList list = files;
bool singleFile(files.size() == 1);
for(int row = 0; row < count; ++row) {
if (list.empty()) {
break;
}
index = model_->index(row, 0);
auto info = model_->fileInfoFromIndex(index);
for(auto it = list.cbegin(); it != list.cend(); ++it) {
auto& item = *it;
if(item == info) {
selectionModel()->select(index, QItemSelectionModel::Select);
if (!firstIndex.isValid()) {
firstIndex = index;
}
list.erase(it);
break;
}
}
}
if (firstIndex.isValid()) {
view->scrollTo(firstIndex, QAbstractItemView::EnsureVisible);
if (singleFile) { // give focus to the single file
selectionModel()->setCurrentIndex(firstIndex, QItemSelectionModel::Current);
}
}
}
Fm::FileInfoList FolderView::selectedFiles() const {
if(model_) {
QModelIndexList selIndexes = mode == DetailedListMode ? selectedRows() : selectedIndexes();
@ -941,7 +995,7 @@ void FolderView::invertSelection() {
}
void FolderView::childDragEnterEvent(QDragEnterEvent* event) {
qDebug("drag enter");
//qDebug("drag enter");
if(event->mimeData()->hasFormat("text/uri-list")) {
event->accept();
}
@ -951,12 +1005,23 @@ void FolderView::childDragEnterEvent(QDragEnterEvent* event) {
}
void FolderView::childDragLeaveEvent(QDragLeaveEvent* e) {
qDebug("drag leave");
//qDebug("drag leave");
e->accept();
}
void FolderView::childDragMoveEvent(QDragMoveEvent* /*e*/) {
qDebug("drag move");
void FolderView::childDragMoveEvent(QDragMoveEvent* e) {
// Since it isn't possible to drop on a file (see "FolderModel::dropMimeData()"),
// we enable the drop indicator only when the cursor is on a folder.
QModelIndex index = view->indexAt(e->pos());
if(index.isValid() && index.model()) {
QVariant data = index.model()->data(index, FolderModel::FileInfoRole);
auto info = data.value<std::shared_ptr<const Fm::FileInfo>>();
if(info && !info->isDir()) {
view->setDropIndicatorShown(false);
return;
}
}
view->setDropIndicatorShown(true);
}
void FolderView::childDropEvent(QDropEvent* e) {
@ -1049,8 +1114,8 @@ bool FolderView::eventFilter(QObject* watched, QEvent* event) {
}
autoSelectionTimer_->start(autoSelectionDelay_);
}
break;
}
break;
case QEvent::HoverLeave:
if(style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) {
setCursor(Qt::ArrowCursor);
@ -1086,6 +1151,42 @@ bool FolderView::eventFilter(QObject* watched, QEvent* event) {
return true;
}
}
// Smooth Scrolling
// Some tricks are adapted from <https://github.com/zhou13/qsmoothscrollarea>.
else if(mode != DetailedListMode
&& event->spontaneous()
&& !(QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::AltModifier))) {
if(QScrollBar* vbar = view->verticalScrollBar()) {
// keep track of the wheel event for smooth scrolling
wheelEvent_ = static_cast<QWheelEvent*>(event);
int delta = wheelEvent_->angleDelta().y();
if((delta > 0 && vbar->value() == vbar->minimum()) || (delta < 0 && vbar->value() == vbar->maximum())) {
break; // the scrollbar can't move
}
// get a rough estimation of the wheel speed and disable animation if it's too high
static QList<qint64> wheelEvents;
wheelEvents << QDateTime::currentMSecsSinceEpoch();
while(wheelEvents.last() - wheelEvents.first() > 500) {
wheelEvents.removeFirst();
}
if(wheelEvents.size() > 10) {
break;
}
if(!smoothScrollTimer_) {
smoothScrollTimer_ = new QTimer();
connect(smoothScrollTimer_, &QTimer::timeout, this, &FolderView::scrollSmoothly);
}
// set the data for smooth scrolling
scollData data;
data.delta = delta;
data.leftFrames = scrollAnimFrames;
queuedScrollSteps_.append(data);
smoothScrollTimer_->start(1000 / SCROLL_FRAMES_PER_SEC);
return true;
}
}
break;
default:
break;
@ -1094,6 +1195,36 @@ bool FolderView::eventFilter(QObject* watched, QEvent* event) {
return QObject::eventFilter(watched, event);
}
void FolderView::scrollSmoothly() {
if(!wheelEvent_ || !view->verticalScrollBar()) {
return;
}
int totalDelta = 0;
QList<scollData>::iterator it = queuedScrollSteps_.begin();
while(it != queuedScrollSteps_.end()) {
if(it->leftFrames == 1) { // find the exact delta for the last frame
totalDelta += it->delta - (scrollAnimFrames - 1) * qRound((qreal)it->delta / (qreal)scrollAnimFrames);
it = queuedScrollSteps_.erase(it);
}
else {
totalDelta += qRound((qreal)it->delta / (qreal)scrollAnimFrames);
-- it->leftFrames;
++it;
}
}
if(totalDelta != 0) {
// as in qevent.cpp -> QWheelEvent::QWheelEvent()
QWheelEvent e(wheelEvent_->pos(), wheelEvent_->globalPos(),
totalDelta,
wheelEvent_->buttons(), Qt::NoModifier, Qt::Vertical);
QApplication::sendEvent(view->verticalScrollBar(), &e);
}
if(queuedScrollSteps_.empty()) {
smoothScrollTimer_->stop();
}
}
// this slot handles auto-selection of items.
void FolderView::onAutoSelectionTimeout() {
if(QApplication::mouseButtons() != Qt::NoButton) {
@ -1218,8 +1349,10 @@ void FolderView::onClipboardDataChange() {
if(!folder()->path().hasUriScheme("search") // skip for search results
&& isCutSelection
&& Fm::isCurrentPidClipboardData(*data)) { // set cut files only with this app
auto cutDirPath = paths.size() > 0 ? paths[0].parent(): FilePath();
if(folder()->path() == cutDirPath) {
auto cutDirPath = paths.size() > 0 ? paths[0].parent() : FilePath();
// set the cut file(s) only if the cutting is done here
if(folder()->path() == cutDirPath
&& selectedFilePaths() == paths) {
model_->setCutFiles(selectionModel()->selection());
}
else if(folder()->hadCutFilesUnset() || folder()->hasCutFiles()) {

@ -29,7 +29,6 @@
#include <libfm/fm.h>
#include "foldermodel.h"
#include "proxyfoldermodel.h"
#include "path.h"
#include "core/folder.h"
@ -102,6 +101,7 @@ public:
Fm::FilePathList selectedFilePaths() const;
bool hasSelection() const;
QModelIndex indexFromFolderPath(const Fm::FilePath& folderPath) const;
void selectFiles(const Fm::FileInfoList& files, bool add = false);
void selectAll();
@ -160,6 +160,7 @@ private Q_SLOTS:
void onAutoSelectionTimeout();
void onSelChangedTimeout();
void onClosingEditor(QWidget* editor, QAbstractItemDelegate::EndEditHint hint);
void scrollSmoothly();
Q_SIGNALS:
void clicked(int type, const std::shared_ptr<const Fm::FileInfo>& file);
@ -181,6 +182,14 @@ private:
QTimer* selChangedTimer_;
// the cell margins in the icon and thumbnail modes
QSize itemDelegateMargins_;
// smooth scrolling:
struct scollData {
int delta;
int leftFrames;
};
QTimer *smoothScrollTimer_;
QWheelEvent *wheelEvent_;
QList<scollData> queuedScrollSteps_;
};
}

@ -1,80 +0,0 @@
/*
* Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "icontheme.h"
#include <libfm/fm.h>
#include <QList>
#include <QIcon>
#include <QtGlobal>
#include <QApplication>
#include <QDesktopWidget>
#include "core/iconinfo.h"
namespace Fm {
static IconTheme* theIconTheme = nullptr; // the global single instance of IconTheme.
IconTheme::IconTheme():
currentThemeName_(QIcon::themeName()) {
// NOTE: only one instance is allowed
Q_ASSERT(theIconTheme == nullptr);
Q_ASSERT(qApp != nullptr); // QApplication should exists before contructing IconTheme.
theIconTheme = this;
// We need to get notified when there is a QEvent::StyleChange event so
// we can check if the current icon theme name is changed.
// To do this, we can filter QApplication object itself to intercept
// signals of all widgets, but this may be too inefficient.
// So, we only filter the events on QDesktopWidget instead.
qApp->desktop()->installEventFilter(this);
}
IconTheme::~IconTheme() {
}
IconTheme* IconTheme::instance() {
return theIconTheme;
}
// check if the icon theme name is changed and emit "changed()" signal if any change is detected.
void IconTheme::checkChanged() {
if(QIcon::themeName() != theIconTheme->currentThemeName_) {
// if the icon theme is changed
theIconTheme->currentThemeName_ = QIcon::themeName();
// invalidate the cached data
Fm::IconInfo::updateQIcons();
Q_EMIT theIconTheme->changed();
}
}
// this method is called whenever there is an event on the QDesktopWidget object.
bool IconTheme::eventFilter(QObject* obj, QEvent* event) {
// we're only interested in the StyleChange event.
// FIXME: QEvent::ThemeChange seems to be interal to Qt 5 and is not documented
if(event->type() == QEvent::StyleChange || event->type() == QEvent::ThemeChange) {
checkChanged(); // check if the icon theme is changed
}
return QObject::eventFilter(obj, event);
}
} // namespace Fm

@ -1,53 +0,0 @@
/*
* Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef FM_ICONTHEME_H
#define FM_ICONTHEME_H
#include "libfmqtglobals.h"
#include <QIcon>
#include <QString>
#include "libfm/fm.h"
namespace Fm {
class LIBFM_QT_API IconTheme: public QObject {
Q_OBJECT
public:
IconTheme();
~IconTheme();
static IconTheme* instance();
static void checkChanged(); // check if current icon theme name is changed
Q_SIGNALS:
void changed(); // emitted when the name of current icon theme is changed
protected:
bool eventFilter(QObject* obj, QEvent* event);
private:
QString currentThemeName_;
};
}
#endif // FM_ICONTHEME_H

@ -5,7 +5,7 @@ includedir=${prefix}/include
Name: libfm-qt
Description: A Qt/glib/gio-based lib used to develop file managers providing some file management utilities. (This is a Qt port of the original libfm library)
URL: http://pcmanfm.sourceforge.net/
URL: https://github.com/lxqt/libfm-qt
Requires: @REQUIRED_QT@ libfm >= 1.2.0
Version: @LIBFM_QT_VERSION@
Libs: -L${libdir} -lfm -l@LIBFM_QT_LIBRARY_NAME@

@ -21,7 +21,6 @@
#include "libfmqt.h"
#include <QLocale>
#include <QPixmapCache>
#include "icontheme.h"
#include "core/thumbnailer.h"
#include "xdndworkaround.h"
@ -31,7 +30,6 @@ struct LibFmQtData {
LibFmQtData();
~LibFmQtData();
IconTheme* iconTheme;
QTranslator translator;
XdndWorkaround workaround;
int refCount;
@ -52,7 +50,6 @@ LibFmQtData::LibFmQtData(): refCount(1) {
fm_init(nullptr);
// turn on glib debug message
// g_setenv("G_MESSAGES_DEBUG", "all", true);
iconTheme = new IconTheme();
Fm::Thumbnailer::loadAll();
translator.load("libfm-qt_" + QLocale::system().name(), LIBFM_QT_DATA_DIR "/translations");
@ -67,7 +64,6 @@ LibFmQtData::~LibFmQtData() {
GVfs* vfs = g_vfs_get_default();
g_vfs_unregister_uri_scheme(vfs, "menu");
g_vfs_unregister_uri_scheme(vfs, "search");
delete iconTheme;
fm_finalize();
}

@ -26,6 +26,7 @@
#include "mountoperationpassworddialog_p.h"
#include "mountoperationquestiondialog_p.h"
#include "ui_mount-operation-password.h"
#include "core/gioptrs.h"
namespace Fm {
@ -83,6 +84,16 @@ MountOperation::~MountOperation() {
// qDebug("MountOperation deleted");
}
void MountOperation::mountEnclosingVolume(const FilePath &path) {
g_file_mount_enclosing_volume(path.gfile().get(), G_MOUNT_MOUNT_NONE, op, cancellable_,
(GAsyncReadyCallback)onMountFileFinished, new QPointer<MountOperation>(this));
}
void MountOperation::mountMountable(const FilePath &mountable) {
g_file_mount_mountable(mountable.gfile().get(), G_MOUNT_MOUNT_NONE, op, cancellable_,
(GAsyncReadyCallback)onMountMountableFinished, new QPointer<MountOperation>(this));
}
void MountOperation::onAbort(GMountOperation* /*_op*/, MountOperation* /*pThis*/) {
}
@ -143,6 +154,15 @@ void MountOperation::onMountFileFinished(GFile* file, GAsyncResult* res, QPointe
delete pThis;
}
void MountOperation::onMountMountableFinished(GFile* file, GAsyncResult* res, QPointer<MountOperation>* pThis) {
if(*pThis) {
GError* error = nullptr;
g_file_mount_mountable_finish(file, res, &error);
(*pThis)->handleFinish(error);
}
delete pThis;
}
void MountOperation::onMountVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer< MountOperation >* pThis) {
if(*pThis) {
GError* error = nullptr;

@ -48,11 +48,15 @@ public:
explicit MountOperation(bool interactive = true, QWidget* parent = 0);
~MountOperation();
FM_QT_DEPRECATED
void mount(const Fm::FilePath& path) {
g_file_mount_enclosing_volume(path.gfile().get(), G_MOUNT_MOUNT_NONE, op, cancellable_,
(GAsyncReadyCallback)onMountFileFinished, new QPointer<MountOperation>(this));
mountEnclosingVolume(path);
}
void mountEnclosingVolume(const Fm::FilePath& path);
void mountMountable(const Fm::FilePath& mountable);
void mount(GVolume* volume) {
g_volume_mount(volume, G_MOUNT_MOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onMountVolumeFinished, new QPointer<MountOperation>(this));
}
@ -135,6 +139,7 @@ private:
// it's possible that this object is freed when the callback is called by gio, so guarding with QPointer is needed here.
static void onMountFileFinished(GFile* file, GAsyncResult* res, QPointer<MountOperation>* pThis);
static void onMountMountableFinished(GFile* file, GAsyncResult* res, QPointer<MountOperation>* pThis);
static void onMountVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer<MountOperation>* pThis);
static void onUnmountMountFinished(GMount* mount, GAsyncResult* res, QPointer<MountOperation>* pThis);
static void onEjectMountFinished(GMount* mount, GAsyncResult* res, QPointer<MountOperation>* pThis);

@ -1,441 +0,0 @@
/*
* Copyright (C) 2016 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef __LIBFM_QT_FM_PATH_H__
#define __LIBFM_QT_FM_PATH_H__
#include <libfm/fm.h>
#include <QObject>
#include <QtGlobal>
#include <QMetaType>
#include "libfmqtglobals.h"
namespace Fm {
class LIBFM_QT_API PathList {
public:
PathList(void ) {
dataPtr_ = reinterpret_cast<FmPathList*>(fm_path_list_new());
}
PathList(FmPathList* dataPtr){
dataPtr_ = dataPtr != nullptr ? reinterpret_cast<FmPathList*>(fm_list_ref(FM_LIST(dataPtr))) : nullptr;
}
// copy constructor
PathList(const PathList& other) {
dataPtr_ = other.dataPtr_ != nullptr ? reinterpret_cast<FmPathList*>(fm_list_ref(FM_LIST(other.dataPtr_))) : nullptr;
}
// move constructor
PathList(PathList&& other) {
dataPtr_ = reinterpret_cast<FmPathList*>(other.takeDataPtr());
}
// destructor
~PathList() {
if(dataPtr_ != nullptr) {
fm_list_unref(FM_LIST(dataPtr_));
}
}
// create a wrapper for the data pointer without increasing the reference count
static PathList wrapPtr(FmPathList* dataPtr) {
PathList obj;
obj.dataPtr_ = reinterpret_cast<FmPathList*>(dataPtr);
return obj;
}
// disown the managed data pointer
FmPathList* takeDataPtr() {
FmPathList* data = reinterpret_cast<FmPathList*>(dataPtr_);
dataPtr_ = nullptr;
return data;
}
// get the raw pointer wrapped
FmPathList* dataPtr() {
return reinterpret_cast<FmPathList*>(dataPtr_);
}
// automatic type casting
operator FmPathList*() {
return dataPtr();
}
// copy assignment
PathList& operator=(const PathList& other) {
if(dataPtr_ != nullptr) {
fm_list_unref(FM_LIST(dataPtr_));
}
dataPtr_ = other.dataPtr_ != nullptr ? reinterpret_cast<FmPathList*>(fm_list_ref(FM_LIST(other.dataPtr_))) : nullptr;
return *this;
}
// move assignment
PathList& operator=(PathList&& other) {
dataPtr_ = reinterpret_cast<FmPathList*>(other.takeDataPtr());
return *this;
}
bool isNull() {
return (dataPtr_ == nullptr);
}
// methods
void writeUriList(GString* buf) {
fm_path_list_write_uri_list(dataPtr(), buf);
}
char* toUriList(void) {
return fm_path_list_to_uri_list(dataPtr());
}
unsigned int getLength() {
return fm_path_list_get_length(dataPtr());
}
bool isEmpty() {
return fm_path_list_is_empty(dataPtr());
}
FmPath* peekHead() {
return fm_path_list_peek_head(dataPtr());
}
GList* peekHeadLink() {
return fm_path_list_peek_head_link(dataPtr());
}
void pushTail(FmPath* path) {
fm_path_list_push_tail(dataPtr(), path);
}
static PathList newFromFileInfoGslist(GSList* fis) {
return PathList::wrapPtr(fm_path_list_new_from_file_info_gslist(fis));
}
static PathList newFromFileInfoGlist(GList* fis) {
return PathList::wrapPtr(fm_path_list_new_from_file_info_glist(fis));
}
static PathList newFromFileInfoList(FmFileInfoList* fis) {
return PathList::wrapPtr(fm_path_list_new_from_file_info_list(fis));
}
static PathList newFromUris(char* const* uris) {
return PathList::wrapPtr(fm_path_list_new_from_uris(uris));
}
static PathList newFromUriList(const char* uri_list) {
return PathList::wrapPtr(fm_path_list_new_from_uri_list(uri_list));
}
private:
FmPathList* dataPtr_; // data pointer for the underlying C struct
};
class LIBFM_QT_API Path {
public:
// default constructor
Path() {
dataPtr_ = nullptr;
}
Path(FmPath* dataPtr){
dataPtr_ = dataPtr != nullptr ? reinterpret_cast<FmPath*>(fm_path_ref(dataPtr)) : nullptr;
}
// copy constructor
Path(const Path& other) {
dataPtr_ = other.dataPtr_ != nullptr ? reinterpret_cast<FmPath*>(fm_path_ref(other.dataPtr_)) : nullptr;
}
// move constructor
Path(Path&& other) {
dataPtr_ = reinterpret_cast<FmPath*>(other.takeDataPtr());
}
// destructor
~Path() {
if(dataPtr_ != nullptr) {
fm_path_unref(dataPtr_);
}
}
// create a wrapper for the data pointer without increasing the reference count
static Path wrapPtr(FmPath* dataPtr) {
Path obj;
obj.dataPtr_ = reinterpret_cast<FmPath*>(dataPtr);
return obj;
}
// disown the managed data pointer
FmPath* takeDataPtr() {
FmPath* data = reinterpret_cast<FmPath*>(dataPtr_);
dataPtr_ = nullptr;
return data;
}
// get the raw pointer wrapped
FmPath* dataPtr() {
return reinterpret_cast<FmPath*>(dataPtr_);
}
// automatic type casting
operator FmPath*() {
return dataPtr();
}
// copy assignment
Path& operator=(const Path& other) {
if(dataPtr_ != nullptr) {
fm_path_unref(dataPtr_);
}
dataPtr_ = other.dataPtr_ != nullptr ? reinterpret_cast<FmPath*>(fm_path_ref(other.dataPtr_)) : nullptr;
return *this;
}
// move assignment
Path& operator=(Path&& other) {
dataPtr_ = reinterpret_cast<FmPath*>(other.takeDataPtr());
return *this;
}
bool isNull() {
return (dataPtr_ == nullptr);
}
// methods
bool isNative() {
return fm_path_is_native(dataPtr());
}
bool isTrash() {
return fm_path_is_trash(dataPtr());
}
bool isTrashRoot() {
return fm_path_is_trash_root(dataPtr());
}
bool isNativeOrTrash() {
return fm_path_is_native_or_trash(dataPtr());
}
int depth(void) {
return fm_path_depth(dataPtr());
}
bool equalStr(const gchar* str, int n) {
return fm_path_equal_str(dataPtr(), str, n);
}
int compare(FmPath* p2) {
return fm_path_compare(dataPtr(), p2);
}
int compare(Path& p2) {
return fm_path_compare(dataPtr(), p2.dataPtr());
}
bool equal(FmPath* p2) {
return fm_path_equal(dataPtr(), p2);
}
bool equal(Path& p2) {
return fm_path_equal(dataPtr(), p2.dataPtr());
}
bool operator == (Path& other) {
return fm_path_equal(dataPtr(), other.dataPtr());
}
bool operator != (Path& other) {
return !fm_path_equal(dataPtr(), other.dataPtr());
}
bool operator < (Path& other) {
return compare(other);
}
bool operator > (Path& other) {
return (other < *this);
}
unsigned int hash(void) {
return fm_path_hash(dataPtr());
}
char* displayBasename(void) {
return fm_path_display_basename(dataPtr());
}
char* displayName(gboolean human_readable) {
return fm_path_display_name(dataPtr(), human_readable);
}
GFile* toGfile(void) {
return fm_path_to_gfile(dataPtr());
}
char* toUri(void) {
return fm_path_to_uri(dataPtr());
}
char* toStr(void) {
return fm_path_to_str(dataPtr());
}
Path getSchemePath(void) {
return Path(fm_path_get_scheme_path(dataPtr()));
}
bool hasPrefix(FmPath* prefix) {
return fm_path_has_prefix(dataPtr(), prefix);
}
FmPathFlags getFlags(void) {
return fm_path_get_flags(dataPtr());
}
Path getParent(void) {
return Path(fm_path_get_parent(dataPtr()));
}
static Path getAppsMenu(void ) {
return Path(fm_path_get_apps_menu());
}
static Path getTrash(void ) {
return Path(fm_path_get_trash());
}
static Path getDesktop(void ) {
return Path(fm_path_get_desktop());
}
static Path getHome(void ) {
return Path(fm_path_get_home());
}
static Path getRoot(void ) {
return Path(fm_path_get_root());
}
static Path newForGfile(GFile* gf) {
return Path::wrapPtr(fm_path_new_for_gfile(gf));
}
Path newRelative(const char* rel) {
return Path::wrapPtr(fm_path_new_relative(dataPtr(), rel));
}
Path newChildLen(const char* basename, int name_len) {
return Path::wrapPtr(fm_path_new_child_len(dataPtr(), basename, name_len));
}
Path newChild(const char* basename) {
return Path::wrapPtr(fm_path_new_child(dataPtr(), basename));
}
static Path newForCommandlineArg(const char* arg) {
return Path::wrapPtr(fm_path_new_for_commandline_arg(arg));
}
static Path newForStr(const char* path_str) {
return Path::wrapPtr(fm_path_new_for_str(path_str));
}
static Path newForDisplayName(const char* path_name) {
return Path::wrapPtr(fm_path_new_for_display_name(path_name));
}
static Path newForUri(const char* uri) {
return Path::wrapPtr(fm_path_new_for_uri(uri));
}
static Path newForPath(const char* path_name) {
return Path::wrapPtr(fm_path_new_for_path(path_name));
}
private:
FmPath* dataPtr_; // data pointer for the underlying C struct
};
}
Q_DECLARE_OPAQUE_POINTER(FmPath*)
#endif // __LIBFM_QT_FM_PATH_H__

@ -305,8 +305,9 @@ void PathBar::openEditor() {
connect(tempPathEdit_, &PathEdit::returnPressed, this, &PathBar::onReturnPressed);
connect(tempPathEdit_, &PathEdit::editingFinished, this, &PathBar::closeEditor);
}
tempPathEdit_->setFocus();
tempPathEdit_->selectAll();
QApplication::clipboard()->setText(tempPathEdit_->text(), QClipboard::Selection);
QTimer::singleShot(0, tempPathEdit_, SLOT(setFocus()));
}
void PathBar::closeEditor() {

@ -112,7 +112,7 @@ bool PathEdit::event(QEvent* e) {
if(keyEvent->key() == Qt::Key_Tab && keyEvent->modifiers() == Qt::NoModifier) { // Tab key is pressed
e->accept();
// do auto-completion when the user press the Tab key.
// This fixes #201: https://github.com/lxde/pcmanfm-qt/issues/201
// This fixes #201: https://github.com/lxqt/pcmanfm-qt/issues/201
autoComplete();
return true;
}

@ -19,7 +19,6 @@
#include "placesmodel.h"
#include "icontheme.h"
#include <gio/gio.h>
#include <QDebug>
#include <QMimeData>
@ -55,25 +54,19 @@ PlacesModel::PlacesModel(QObject* parent):
createTrashItem();
// FIXME: add an option to hide network:///
if(true) {
computerItem = new PlacesModelItem("computer", tr("Computer"), Fm::FilePath::fromUri("computer:///"));
placesRoot->appendRow(computerItem);
}
else {
computerItem = nullptr;
}
computerItem = new PlacesModelItem("computer", tr("Computer"), Fm::FilePath::fromUri("computer:///"));
placesRoot->appendRow(computerItem);
// FIXME: add an option to hide applications:///
const char* applicaion_icon_names[] = {"system-software-install", "applications-accessories", "application-x-executable"};
// NOTE: g_themed_icon_new_from_names() accepts char**, but actually const char** is OK.
Fm::GIconPtr gicon{g_themed_icon_new_from_names((char**)applicaion_icon_names, G_N_ELEMENTS(applicaion_icon_names)), false};
auto fmicon = Fm::IconInfo::fromGIcon(std::move(gicon));
applicationsItem = new PlacesModelItem(fmicon, tr("Applications"), Fm::FilePath::fromUri("menu:///applications/"));
placesRoot->appendRow(applicationsItem);
{ // Applications
const char* applicaion_icon_names[] = {"system-software-install", "applications-accessories", "application-x-executable"};
// NOTE: g_themed_icon_new_from_names() accepts char**, but actually const char** is OK.
Fm::GIconPtr gicon{g_themed_icon_new_from_names((char**)applicaion_icon_names, G_N_ELEMENTS(applicaion_icon_names)), false};
auto fmicon = Fm::IconInfo::fromGIcon(std::move(gicon));
applicationsItem = new PlacesModelItem(fmicon, tr("Applications"), Fm::FilePath::fromUri("menu:///applications/"));
placesRoot->appendRow(applicationsItem);
}
// FIXME: add an option to hide network:///
if(true) {
{ // Network
const char* network_icon_names[] = {"network", "folder-network", "folder"};
// NOTE: g_themed_icon_new_from_names() accepts char**, but actually const char** is OK.
Fm::GIconPtr gicon{g_themed_icon_new_from_names((char**)network_icon_names, G_N_ELEMENTS(network_icon_names)), false};
@ -81,9 +74,6 @@ PlacesModel::PlacesModel(QObject* parent):
networkItem = new PlacesModelItem(fmicon, tr("Network"), Fm::FilePath::fromUri("network:///"));
placesRoot->appendRow(networkItem);
}
else {
networkItem = nullptr;
}
devicesRoot = new QStandardItem(tr("Devices"));
devicesRoot->setSelectable(false);
@ -171,7 +161,7 @@ PlacesModel::~PlacesModel() {
g_object_unref(trashMonitor_);
}
Q_FOREACH(GMount* mount, shadowedMounts_) {
for(GMount* const mount : qAsConst(shadowedMounts_)) {
g_object_unref(mount);
}
}
@ -429,6 +419,18 @@ void PlacesModel::onMountRemoved(GVolumeMonitor* monitor, GMount* mount, PlacesM
}
void PlacesModel::onVolumeAdded(GVolumeMonitor* /*monitor*/, GVolume* volume, PlacesModel* pThis) {
// the item may have been added with "mount-added" (as in loopback mounting)
bool itemExists = false;
GMount* mount = g_volume_get_mount(volume);
if(mount) {
if(pThis->itemFromMount(mount)) {
itemExists = true;
}
g_object_unref(mount);
}
if(itemExists) {
return;
}
// for some unknown reasons, sometimes we get repeated volume-added
// signals and added a device more than one. So, make a sanity check here.
PlacesModelVolumeItem* volumeItem = pThis->itemFromVolume(volume);

@ -19,7 +19,6 @@
#include "placesmodelitem.h"
#include "icontheme.h"
#include <gio/gio.h>
#include <QPainter>
@ -87,7 +86,7 @@ QVariant PlacesModelItem::data(int role) const {
}
PlacesModelBookmarkItem::PlacesModelBookmarkItem(std::shared_ptr<const Fm::BookmarkItem> bm_item):
PlacesModelItem{Fm::IconInfo::fromName("folder"), bm_item->name(), bm_item->path()},
PlacesModelItem{bm_item->icon(), bm_item->name(), bm_item->path()},
bookmarkItem_{std::move(bm_item)} {
setEditable(true);
}

@ -32,12 +32,112 @@
namespace Fm {
std::shared_ptr<PlacesProxyModel> PlacesView::proxyModel_;
PlacesProxyModel::PlacesProxyModel(QObject* parent) :
QSortFilterProxyModel(parent),
showAll_(false),
hiddenItemsRestored_(false) {
}
PlacesProxyModel::~PlacesProxyModel() {
}
void PlacesProxyModel::restoreHiddenItems(const QSet<QString>& items) {
// hidden items should be restored only once
if(!hiddenItemsRestored_ && !items.isEmpty()) {
hidden_.clear();
QSet<QString>::const_iterator i = items.constBegin();
while (i != items.constEnd()) {
if(!(*i).isEmpty()) {
hidden_ << *i;
}
++i;
}
hiddenItemsRestored_ = true;
invalidateFilter();
}
}
void PlacesProxyModel::setHidden(const QString& str, bool hide) {
if(hide) {
if(!str.isEmpty()) {
hidden_ << str;
}
}
else {
hidden_.remove(str);
}
invalidateFilter();
}
void PlacesProxyModel::showAll(bool show) {
showAll_ = show;
invalidateFilter();
}
bool PlacesProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const {
if(showAll_ || hidden_.isEmpty()) {
return true;
}
if(PlacesModel* srcModel = static_cast<PlacesModel*>(sourceModel())) {
QModelIndex index = srcModel->index(source_row, 0, source_parent);
if(PlacesModelItem* item = static_cast<PlacesModelItem*>(srcModel->itemFromIndex(index))) {
if(item->type() == PlacesModelItem::Places) {
if(auto path = item->path()) {
if(hidden_.contains(path.toString().get())) {
return false;
}
}
}
else if(item->type() == PlacesModelItem::Volume) {
CStrPtr uuid{g_volume_get_uuid(static_cast<PlacesModelVolumeItem*>(item)->volume())};
if(uuid && hidden_.contains(uuid.get())) {
return false;
}
}
// show a root items only if, at least, one of its children is shown
else if((source_row == 0 || source_row == 1) && !source_parent.isValid()) {
QModelIndex indx = index.child(0, 0);
while(PlacesModelItem* childItem = static_cast<PlacesModelItem*>(srcModel->itemFromIndex(indx))) {
if(childItem->type() == PlacesModelItem::Places) {
if(auto path = childItem->path()) {
if(!hidden_.contains(path.toString().get())) {
return true;
}
}
}
else if(childItem->type() == PlacesModelItem::Volume) {
CStrPtr uuid{g_volume_get_uuid(static_cast<PlacesModelVolumeItem*>(childItem)->volume())};
if(uuid == nullptr || !hidden_.contains(uuid.get())) {
return true;
}
}
else {
return true;
}
indx = indx.sibling(indx.row() + 1, 0);
}
return false;
}
}
}
return true;
}
PlacesView::PlacesView(QWidget* parent):
QTreeView(parent) {
setRootIsDecorated(false);
setHeaderHidden(true);
setIndentation(12);
/* merge with the surroundings */
setFrameShape(QFrame::NoFrame);
QPalette p = palette();
p.setColor(QPalette::Base, QColor(Qt::transparent));
setPalette(p);
viewport()->setAutoFillBackground(false);
connect(this, &QTreeView::clicked, this, &PlacesView::onClicked);
connect(this, &QTreeView::pressed, this, &PlacesView::onPressed);
@ -49,7 +149,27 @@ PlacesView::PlacesView(QWidget* parent):
setItemDelegateForColumn(0, delegate);
model_ = PlacesModel::globalInstance();
setModel(model_.get());
if(!proxyModel_) {
proxyModel_ = std::make_shared<PlacesProxyModel>();
}
if(!proxyModel_->sourceModel()) { // all places-views may have been closed
proxyModel_->setSourceModel(model_.get());
}
setModel(proxyModel_.get());
// these 2 connections are needed to update filtering
connect(model_.get(), &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex&, int, int) {
proxyModel_->setHidden(QString()); // just invalidates filter
expandAll();
// for some reason (a Qt bug?), spanning is reset
setFirstColumnSpanned(0, QModelIndex(), true);
setFirstColumnSpanned(1, QModelIndex(), true);
setFirstColumnSpanned(2, QModelIndex(), true);
});
connect(model_.get(), &QAbstractItemModel::rowsRemoved, this, [this](const QModelIndex&, int, int) {
proxyModel_->setHidden(QString());
});
QHeaderView* headerView = header();
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
@ -84,7 +204,7 @@ void PlacesView::activateRow(int type, const QModelIndex& index) {
if(!index.parent().isValid()) { // ignore root items
return;
}
PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(index));
PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(proxyModel_->mapToSource(index)));
if(item) {
auto path = item->path();
if(!path) {
@ -155,10 +275,10 @@ void PlacesView::onClicked(const QModelIndex& index) {
activateRow(0, index);
}
else if(index.column() == 1) { // column 1 contains eject buttons of the mounted devices
if(index.parent() == model_->devicesRoot->index()) { // this is a mounted device
if(index.parent() == proxyModel_->mapFromSource(model_->devicesRoot->index())) { // this is a mounted device
// the eject button is clicked
QModelIndex itemIndex = index.sibling(index.row(), 0); // the real item is at column 0
PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(itemIndex));
PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(proxyModel_->mapToSource(itemIndex)));
if(item) {
// eject the volume or the mount
onEjectButtonClicked(item);
@ -176,7 +296,7 @@ void PlacesView::setCurrentPath(Fm::FilePath path) {
// TODO: search for item with the path in model_ and select it.
PlacesModelItem* item = model_->itemFromPath(currentPath_);
if(item) {
selectionModel()->select(item->index(), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
selectionModel()->select(proxyModel_->mapFromSource(item->index()), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
}
else {
clearSelection();
@ -252,7 +372,7 @@ void PlacesView::onDeleteBookmark() {
// virtual
void PlacesView::commitData(QWidget* editor) {
QTreeView::commitData(editor);
PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(model_->itemFromIndex(currentIndex()));
PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(model_->itemFromIndex(proxyModel_->mapToSource(currentIndex())));
auto bookmarkItem = item->bookmark();
// rename bookmark
Fm::Bookmarks::globalInstance()->rename(bookmarkItem, item->text());
@ -287,8 +407,8 @@ void PlacesView::onRenameBookmark() {
}
PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(model_->itemFromIndex(action->index()));
setFocus();
setCurrentIndex(item->index());
edit(item->index());
setCurrentIndex(proxyModel_->mapFromSource(item->index()));
edit(proxyModel_->mapFromSource(item->index()));
}
void PlacesView::onMountVolume() {
@ -338,21 +458,22 @@ void PlacesView::onEjectVolume() {
void PlacesView::contextMenuEvent(QContextMenuEvent* event) {
QModelIndex index = indexAt(event->pos());
if(index.isValid() && index.parent().isValid()) {
if(index.isValid()) {
if(index.column() != 0) { // the real item is at column 0
index = index.sibling(index.row(), 0);
}
// Do not take the ownership of the menu since
// it will be deleted with deleteLater() upon hidden.
// This is possibly related to #145 - https://github.com/lxde/pcmanfm-qt/issues/145
// This is possibly related to #145 - https://github.com/lxqt/pcmanfm-qt/issues/145
QMenu* menu = new QMenu();
QAction* action;
PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(index));
QAction* action = nullptr;
PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(proxyModel_->mapToSource(index)));
if(item->type() != PlacesModelItem::Mount
&& (item->type() != PlacesModelItem::Volume
|| static_cast<PlacesModelVolumeItem*>(item)->isMounted())) {
if(index.parent().isValid()
&& item->type() != PlacesModelItem::Mount
&& (item->type() != PlacesModelItem::Volume
|| static_cast<PlacesModelVolumeItem*>(item)->isMounted())) {
action = new PlacesModel::ItemAction(item->index(), tr("Open in New Tab"), menu);
connect(action, &QAction::triggered, this, &PlacesView::onOpenNewTab);
menu->addAction(action);
@ -364,11 +485,40 @@ void PlacesView::contextMenuEvent(QContextMenuEvent* event) {
switch(item->type()) {
case PlacesModelItem::Places: {
auto path = item->path();
auto path_str = path.toString();
// FIXME: inefficient
if(path && strcmp(path_str.get(), "trash:///") == 0) {
action = new PlacesModel::ItemAction(item->index(), tr("Empty Trash"), menu);
connect(action, &QAction::triggered, this, &PlacesView::onEmptyTrash);
if(path) {
auto path_str = path.toString();
if(strcmp(path_str.get(), "trash:///") == 0) {
action = new PlacesModel::ItemAction(item->index(), tr("Empty Trash"), menu);
auto icn = item->icon();
if(icn && icn->qicon().name() == QLatin1String("user-trash")) { // surely an empty trash
action->setEnabled(false);
}
else {
connect(action, &QAction::triggered, this, &PlacesView::onEmptyTrash);
}
// add the "Empty Trash" item on the top
QList<QAction*> actions = menu->actions();
if(!actions.isEmpty()) {
menu->insertAction(actions.at(0), action);
menu->insertSeparator(actions.at(0));
}
else { // impossible
menu->addAction(action);
}
}
// add a "Hide" action to the end
menu->addSeparator();
action = new PlacesModel::ItemAction(item->index(), tr("Hide"), menu);
QString pathStr(path_str.get());
action->setCheckable(true);
if(proxyModel_->isShowingAll()) {
action->setChecked(proxyModel_->isHidden(pathStr));
}
connect(action, &QAction::triggered, [this, pathStr](bool checked) {
proxyModel_->setHidden(pathStr, checked);
Q_EMIT hiddenItemSet(pathStr, checked);
});
menu->addAction(action);
}
break;
@ -411,6 +561,22 @@ void PlacesView::contextMenuEvent(QContextMenuEvent* event) {
connect(action, &QAction::triggered, this, &PlacesView::onEjectVolume);
menu->addAction(action);
}
// add a "Hide" action to the end
CStrPtr uuid{g_volume_get_uuid(static_cast<PlacesModelVolumeItem*>(item)->volume())};
if(uuid) {
QString str = uuid.get();
menu->addSeparator();
action = new PlacesModel::ItemAction(item->index(), tr("Hide"), menu);
action->setCheckable(true);
if(proxyModel_->isShowingAll()) {
action->setChecked(proxyModel_->isHidden(str));
}
connect(action, &QAction::triggered, [this, str](bool checked) {
proxyModel_->setHidden(str, checked);
Q_EMIT hiddenItemSet(str, checked);
});
menu->addAction(action);
}
break;
}
case PlacesModelItem::Mount: {
@ -420,6 +586,21 @@ void PlacesView::contextMenuEvent(QContextMenuEvent* event) {
break;
}
}
// also add an acton for showing all hidden items
if(proxyModel_->hasHidden()) {
if(item->type() == PlacesModelItem::Bookmark) {
menu->addSeparator();
}
action = new PlacesModel::ItemAction(item->index(), tr("Show All Entries"), menu);
action->setCheckable(true);
action->setChecked(proxyModel_->isShowingAll());
connect(action, &QAction::triggered, [this](bool checked) {
showAll(checked);
});
menu->addAction(action);
}
if(menu->actions().size()) {
menu->popup(mapToGlobal(event->pos()));
connect(menu, &QMenu::aboutToHide, menu, &QMenu::deleteLater);
@ -430,5 +611,19 @@ void PlacesView::contextMenuEvent(QContextMenuEvent* event) {
}
}
void PlacesView::restoreHiddenItems(const QSet<QString>& items) {
proxyModel_->restoreHiddenItems(items);
}
void PlacesView::showAll(bool show) {
proxyModel_->showAll(show);
if(show) {
expandAll();
// for some reason (a Qt bug?), spanning is reset
setFirstColumnSpanned(0, QModelIndex(), true);
setFirstColumnSpanned(1, QModelIndex(), true);
setFirstColumnSpanned(2, QModelIndex(), true);
}
}
} // namespace Fm

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

Loading…
Cancel
Save