Compare commits
No commits in common. "ubuntu/plucky" and "debian/0.12.0-6" have entirely different histories.
ubuntu/plu
...
debian/0.1
6
AUTHORS
Normal file
6
AUTHORS
Normal file
@ -0,0 +1,6 @@
|
||||
Upstream Authors:
|
||||
LXQt team: http://lxqt.org
|
||||
Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
||||
|
||||
Copyright:
|
||||
Copyright (c) 2013-2017 LXQt team
|
591
CHANGELOG
Normal file
591
CHANGELOG
Normal file
@ -0,0 +1,591 @@
|
||||
|
||||
libfm-qt-0.12.0 / 2017-10-21
|
||||
============================
|
||||
|
||||
* Add data transferred to file operation dialog.
|
||||
* Bump versions
|
||||
* Disable context-menu actions that cannot be used
|
||||
* Don't export github templates
|
||||
* Fix partially visible toggled path buttons
|
||||
* Add functions to get and set search settings
|
||||
* Fix mistakes in listview column width calculation
|
||||
* Add archiver separator only when needed
|
||||
* Add a separator before archiver actions
|
||||
* Enable XDS subfolder drop
|
||||
* UI improvements for Fm::MountOperationPasswordDialog()
|
||||
* Respect inactiveness when drawing text
|
||||
* Grey out files that have been Ctrl-X'ed (#88)
|
||||
* Ignore button for error dialog
|
||||
* Inline renaming for detailed list view (#110)
|
||||
* Remove redundant code.
|
||||
* Prefer local paths if they exist
|
||||
* Removed QFileInfo (as @PCMan recommended)
|
||||
* Simplification, optimization and a fix
|
||||
* Really focus text entry on showing dialog
|
||||
* Two small fixes
|
||||
* Keep selection on reloading (if not CPU-intensive)
|
||||
* Added back/forward buttons and fixed 3 issues
|
||||
* Reload button, hidden shortcut and a fix
|
||||
* Implement FileDialog::selectMimeTypeFilter() and QString FileDialog::selectedMimeTypeFilter().
|
||||
* Initialize folder_ to null
|
||||
* Fixed the quote issue
|
||||
* Always preserve explicitly set labels
|
||||
* Update OK button text and state when needed
|
||||
* Initialize FileInfo::isShortcut_ (#113)
|
||||
* Set the selected index current
|
||||
* Fixd open/save and overwrite prompt
|
||||
* Set open/save text
|
||||
* Several important fixes
|
||||
* Added a missing change
|
||||
* Preliminary fixes
|
||||
* Hide UI implementation details for Fm::FileDialog.
|
||||
* Revert the backward incompatible changes in the constructor of Fm::FolderView.
|
||||
* Fix a bug in creating new folder for Fm::FileDialog.
|
||||
* Implement toolbar and quick view mode switches for the Fm::FileDialog class.
|
||||
* Correctly check file types and test the existence of the selected files as needed.
|
||||
* Correctly handle item activation.
|
||||
* Correctly handle filename selection for Fm::FileDialog.
|
||||
* Correctly handle selected files.
|
||||
* Implement filename filtering for Fm::FileDialog.
|
||||
* Check nullity of FileInfo before calling FolderMenu
|
||||
* Arrange Custom Actions
|
||||
* Support custom folder icons
|
||||
* Fix multiple pasting of the same cut file(s)
|
||||
* Fix KDE clipboard tag for cut file(s)
|
||||
* Add a basic skeleton for Fm::FileDialog.
|
||||
* Check nullity of QMimeData (#109)
|
||||
* MountOperationQuestionDialog: Fix handling responses
|
||||
* Fix all height issues in horizontal layouts (#103)
|
||||
* Removed a redundant variable (left there from old tests)
|
||||
* Fix major bugs in Directory Tree
|
||||
* Consider desktop text color, now that everything is done here
|
||||
* Inline Renaming
|
||||
* Fix compact view regression (#102)
|
||||
* Fix detailed list crash on double clicking folders
|
||||
* Removed my garbage
|
||||
* Fixed issues about spacings and click region
|
||||
* Make Fm::FolderItemDelegate support painting text shadows and margins so it can completely replace PCManFM::DesktopItemDelegate.
|
||||
* Avoid using grid size on QListView since this disables any spacing settings.
|
||||
* liblxqt make no sense for libfm-qt
|
||||
* Copied issue template
|
||||
* Add noexcept to move constructors and operator=() so they can be used with STL containers.
|
||||
* FolderView: Optimize selectAll() (#97)
|
||||
* Emit fileSizeChanged when needed
|
||||
* Drops Qt5Core_VERSION_STRING (#96)
|
||||
* Update size column info (#90)
|
||||
* Fix Detailed List view DND (#94)
|
||||
* folderview: Don't allow D&D by Back or Forward
|
||||
* More fixes (#87)
|
||||
* Added a missing change signal (#86)
|
||||
* Fix single items when seaching (#85)
|
||||
* Check for nullity of IconInfo (#84)
|
||||
* Address compiler warnings
|
||||
* Remove addRoots() return type
|
||||
* Remove the unused data/translations/ entry
|
||||
* Fix broken folder unmount message caused by incorrect FilePath & GFile* comparison. (#80)
|
||||
* Remove some superfluous semicolons that lead to pedantic warnings (#79)
|
||||
* Ensure one item per file (#77)
|
||||
* Fix the broken filesystem status (free disk space) display. (#78)
|
||||
* Don't make items current on mouseover (#72)
|
||||
* Fixes a FTBFS in superbuild mode (#75)
|
||||
* Replace start tilde in PathEdit (#73)
|
||||
* Really cancel pending thumbnail jobs on chdir (#74)
|
||||
* Move fixes (#70)
|
||||
* Fix invalid pointers (#69)
|
||||
* Continue file selection on next/previous row (#76)
|
||||
* Code reformat: use 4-space indentation to match the coding style of other LXQt components.
|
||||
* Make all constructors explicit so we don't get unwanted object construction by C++.
|
||||
* Prevent a crash since GObjectPtr's move ctor frees resources
|
||||
* GObjectPtr: Detect & handle "self-assignment"
|
||||
* Fix compatibility with Qt 5.6.
|
||||
* No thumbnail for thumbnails
|
||||
* Fix thumbnail update
|
||||
* Fixed `PathBar::setPath()`
|
||||
* Use real name for renaming
|
||||
* Prevent a crash plus fallback icon
|
||||
* Fix custom actions
|
||||
* volumemanager: Return IconInfo as shared_ptr
|
||||
* FolderModelItem: Check IconInfo existence
|
||||
* Bookmarks: Check validity of insert position
|
||||
* Fix a potential crash of bookmark items when the format of the bookmark file is not correct.
|
||||
* Only load desktop entry files for native filesystems.
|
||||
* Fix the missing icon and display name handling of desktop entry files in Fm::FileInfo.
|
||||
* IconEngine: Use weak_ptr for the parent IconInfo
|
||||
* PathBar: Avoid leak upon QLayout::replaceWidget()
|
||||
* Use const iterators
|
||||
* Use the new lxqt-build-tools new FindExif module
|
||||
* Fix the incorrect header inclusion in fileoperation.cpp.
|
||||
* Fix incorrect #include of libfmqtglobals.h.
|
||||
* Fix a bug when copying to and pasting from "x-special/gnome-copied-files" mime-type.
|
||||
* Fix bugs in the Custom Actions C++ port.
|
||||
* Try to port libfm custom actions to C++.
|
||||
* Try to update the content of a folder after its mount status is changed. Handle cancelled dir listing job properly.
|
||||
* Rename namespace Fm2 to Fm.
|
||||
* Remove unused header files of the old C API wrappers.
|
||||
* Fix bugs in search:// support and finish porting file searching to C++. Fix several bugs in Fm2::FileInfo which caused by null parent dir.
|
||||
* Add a missing test case for places view.
|
||||
* Try to add support for menu:// and search:// URI scheme implemented in libfm.
|
||||
* Correctly destroy Fm2::Job objects when their worker threads terminate.
|
||||
* Fix incorrect handling of PathBar button creation which causes infinite loop when the underlying GFile implementation contains bugs.
|
||||
* Fix incorrect path of application menu URI.
|
||||
* Fix QThread related bugs in PathEdit which causes memory leaks.
|
||||
* Fix a bug in DirTreeModelItem causing crashes. Also speed up batch insertion of large amount of items.
|
||||
* Use const iterators (#61)
|
||||
* Fix the broken folder reload().
|
||||
* Make all Fm2::Job derived classes reimplement exec() instead of run() method. The new run() method will emit finished() signal automatically when exec() returns.
|
||||
* Fix memory leaks caused by incorrect use of QThread.
|
||||
* Fix a memory leak in Fm::ThumbnailJob.
|
||||
* Fix memory leaks caused by broken cache.
|
||||
* Fix wrong size of generated thumbnails by external thumbnailers.
|
||||
* Fix memory bugs in Fm2::GErrorPtr and improve the error handling of Fm2::Job and Fm2::Folder.
|
||||
* Fix some errors related to incorrect use of std::remove() in Fm2::Folder. Replace QList with std::vector and use binary search when inserting items for the Fm::DirTreeModelItem.
|
||||
* Change the handling of Fm::FolderView::selChanged signal to make it more efficient.
|
||||
* Port to the new Fm2::TotalSizeJob API.
|
||||
* Fix compatibility with libfm custom actions.
|
||||
* Add some compatibility API which helps migrating old C APIs to the new C++ implementation.
|
||||
* Convert datetime to locale-aware strings using QDateTime.
|
||||
* Use QCollator to perform file sorting.
|
||||
* Fix detailed view.
|
||||
* Finish porting DirTreeModel to the new API. Fix bugs in Fm2::FilePath and Fm2::FileInfo APIs.
|
||||
* Port the libfm terminal emulator related APIs to C++.
|
||||
* Rename some methods in Fm2::Folder and Fm2::FileInfo for consistency.
|
||||
* Port to the new IconInfo API and deprecate manual icon update when the theme is changed.
|
||||
* Rename Fm::Icon to Fm::IconInfo.
|
||||
* Port emblem support to the new libfm C++ API.
|
||||
* Remove unused files, including some old APIs. Replace QVector in BrowseHistory with STL vector.
|
||||
* Fix a bug in Fm::FileMenu.
|
||||
* Port file-click handling to the new C++ API.
|
||||
* Fix bugs in Fm::PathBar getting wrong path when a path button is toggled.
|
||||
* Remove Fm::FilePath(const char* path_str) to avoid ambiguity.
|
||||
* Replace all NULL with C++ 11 nullptr;
|
||||
* Fix FilePath related errors caused by incomplete porting.
|
||||
* Make Fm::FolderConfig support the new Fm::FilePath class.
|
||||
* Fix Fm::BookmarkAction to use the new C++ API.
|
||||
* Fix missing icons of places view caused by memory errors.
|
||||
* Fix memory errors in Fm2::Bookmarks::reorder(). Add a small test case for the places view.
|
||||
* Share the same places model among all views.
|
||||
* Port most of the existing UI-related APIs to the new C++ APIs (excluding the file operations).
|
||||
* Port the path bar to the new Fm2 API.
|
||||
* Implement VolumeManager class which is a QObject wrapper of gio GVolumeMonitor.
|
||||
* Add some getters for Volume and Mount classes.
|
||||
* Properly associate external thumbnailers with mimetypes they support and fix thumbnail generation from thumbnailers.
|
||||
* Start porting thumbnail loaders to the new C++ APIs. Add new Fm::ThumbnailJob used to load thumbnails for a given file list. Add commonDirPath paramter to Fm::FileInfoJob to reduce memory usage.
|
||||
* Add the missing test case for folder view.
|
||||
* Start porting Fm::FolderModel and Fm::FolderView to the new libfm core c++ API.
|
||||
* Work in progress.
|
||||
* Add a c++ wrapper for GFileMonitor. Add LIBFM_QT_API declaration for all public headers.
|
||||
* Port error handling mechanism of FmJob to C++ and improve the GError wrapper class.
|
||||
* Bump year
|
||||
* Add gioptrs.h which defines smart pointer types wrapping gio related data types. Add some basic skeleton for various I/O jobs classes.
|
||||
* Start porting Copyjob and add basic skeleton for untrash and symlink jobs.
|
||||
* Finish porting FmFolder to C++.
|
||||
* Add a very simple test case to test the new Fm core C++ code. Fix bugs in smart pointers and do empty base class optimization for CStrPtr.
|
||||
* Improve Fm::Folder.
|
||||
* Rename UserInfo to UserInfoCache.
|
||||
* Port Fm::Bookmarks to C++.
|
||||
* Port FmDeepCountJob to C++.
|
||||
* Add basic skeletion to Fm::VolumeManager.
|
||||
* Implement Fm2::UserInfo, which is a cache for uid/gid name mapping.
|
||||
* Add basic skeleton for other C++ classes waiting for porting.
|
||||
* Add GSignalHandler to do automatic signal/slot connection management with type safety for GObject.
|
||||
* Add basic skeleton for the C++ 11 port of FmFileInfoJob.
|
||||
* Try to port Fm::Folder and Fm::DirListJob to Qt5 and C++ 11.
|
||||
* Try to port FmIcon, FmFileInfo, and FmMimeType of libfm to clean C++.
|
||||
* Add smart pointer for GObject-derived classes and add Fm::FilePath class which wraps GFile.
|
||||
|
||||
0.11.2 / 2016-12-21
|
||||
===================
|
||||
|
||||
* Release 0.11.2: Update changelog
|
||||
* Fix enabled state of path arrows on starting (#58)
|
||||
* bump patch version (#56)
|
||||
* Use QByteArray::constData() where we can (#57)
|
||||
* Updates lxqt-build-tools required version
|
||||
* Bump ABI so version numbers preparing for a new release.
|
||||
* Fix Pathbar Paint on Menu Pop-Up
|
||||
* Code cleanup and refactor for Fm::PathBar.
|
||||
* Added another condition
|
||||
* Added a missing condition (typo)
|
||||
* Scroll Pathbar with Mouse Wheel
|
||||
* Reduct flickering of the path bar when creating path buttons.
|
||||
* Code simplification by removing unnecessary signal/slot usage.
|
||||
* Path Button Middle Click
|
||||
* Enable auto-repeat for pathbar scroll buttons.
|
||||
* Make the path bar buttons aware of style changes.
|
||||
* Use widget style instead of app style
|
||||
* Align Path Buttons
|
||||
* Move FindXCB.cmake to lxqt-build-tools
|
||||
* Adds superbuild/intree support
|
||||
* Removes not needed dependency check
|
||||
* Set CMP0024 policy usage to NEW
|
||||
* Updates target_include_directories() stuff
|
||||
* Drops GLib library detection
|
||||
* Use the new FindMenuCache CMake module
|
||||
* Use the new FindFm CMake module
|
||||
* Check for FolderModelItem info (and FmPath)
|
||||
* Add Fm::PathBar::editingFinished() signal.
|
||||
* Select the current path when editing the path bar.
|
||||
* Enable path editing and popup menu for the button-style path bar.
|
||||
* Properly set styles of path buttons.
|
||||
* Remove unnecessary debug messages.
|
||||
* Try to implement the Fm::PathBar which shows a filesystem path as buttons.
|
||||
* Adds Build PROJECT_NAME with Qt version message
|
||||
* Move LIBFM_DATA_DIR to pcmanfm repo.
|
||||
* Refactors CUSTOM_ACTIONS compile definition
|
||||
* Refactors LIBFM_DATA_DIR compile definition
|
||||
* Drop add_definitions. Use target_compile_definitions.
|
||||
* Removes duplicated symbols visibility settings
|
||||
* README.md: Add build dependency lxqt-build-tools
|
||||
* Use the new lxqt-build-tools package
|
||||
* Restore symlink emblem
|
||||
* Remove empty files
|
||||
* Try to refactor the emblemed icon support in a more generalized way. Reuse FolderItemDelegate to paint the emblemed icons in Fm::PlacesView to prevent code duplication. APIs changes: * Add Fm::IconTheme::emblems() and cache emblem info in the cache. * Add Fm::FolderItemDelegate::setFileInfoRole() and Fm::FolderItemDelegate::setFmIconRole() * Cache multiple emblems rather than getting the first one only (but only paint the first one now). * Remove icon sizes from Fm::PlacesModel and Fm::PlacesModelItems to maintain MVC design pattern and backward incompatibility. * Expose two role IDs in Fm::PlacesModel: FileInfoRole and FmIconRole so the views can access these data.
|
||||
* Show File Emblems
|
||||
* Emblem For (Encrypted) Volume Icons
|
||||
* Remove cpack (#44)
|
||||
* Also Consider GEmblemedIcon (#41)
|
||||
|
||||
0.11.1 / 2016-09-24
|
||||
===================
|
||||
|
||||
* Release 0.11.1: Add changelog
|
||||
* Bump version to 0.11.1 (#39)
|
||||
* Fix Custom Actions Submenu (#38)
|
||||
* Extend README.md
|
||||
* Add C++ wrappers for libfm C APIs.
|
||||
* Correct positioning of small icons in icon/thumbnail mode (#32)
|
||||
* Silence new compiler warnings (#36)
|
||||
* Adapt to QT_USE_QSTRINGBUILDER macro
|
||||
* Fixes xcb linking error
|
||||
* Use LXQtCompilerSettings cmake module
|
||||
* Replaces deprecated QStyleOptionViewItemV4
|
||||
* Fix item text direction with RTL layout (#33)
|
||||
* Set tabstops for mount operation password dialog (#31)
|
||||
* Fix memory leak in file open menu (#29)
|
||||
* Fixes https://github.com/lxde/pcmanfm-qt/issues/351. (#27)
|
||||
* build: Use external translations
|
||||
* ts-file removal (#26)
|
||||
* Fix memory leaks in file menu (#25)
|
||||
* Merge pull request #24 from philippwiesemann/fix-folder-menu-leak
|
||||
* translations: fix four typos in messages (#23)
|
||||
* Fix memory leak if restoring files from trash
|
||||
* Fix rename dialog displaying source info twice
|
||||
* Enable renaming in Properties dialog Fixes https://github.com/lxde/pcmanfm-qt/issues/324.
|
||||
* Cancel indexing job when closing filepropsdialog
|
||||
* Bump year
|
||||
|
||||
0.11.0 / 2016-02-27
|
||||
===================
|
||||
|
||||
* Bump version number and ABI version and preparing for the initial release.
|
||||
* Redirect focus operation on the folderview to the actual widget
|
||||
* Use grid layout to have proper properties alignment
|
||||
* Focus automatically on the first field of the filesearch dialog
|
||||
* Add (folder) custom actions to foldermenu.
|
||||
* Update czech translation (by Petr Balíček <pbalicek@seznam.cz>)
|
||||
* Fix compiling with Qt < 5.4
|
||||
* Add supports for freedesktop Xdnd direct save (XDS) protocol (closes #pcmanfm-qt/298). * Move Xdnd workarounds from pcmanfm-qt to libfm-qt.
|
||||
* Protected FolderView methods for getting and setting item delegate margins
|
||||
* Perform auto-completion for the path bar when the user press Tab key. This closes pcmanfm-qt/#201.
|
||||
* Disable unused options in file search dialog
|
||||
* Let the style engine draw the text selection rectangle.
|
||||
* Fix file search with smaller than option
|
||||
* Fix target language in Lithuanian translation file
|
||||
* Fix memory leak if reading stream for image failed
|
||||
* Update German translation
|
||||
* Polish translation updated
|
||||
* Polish translation updated
|
||||
* Add a missing type casting to fix a compiler warning.
|
||||
* Relicense libfm-qt to LGPL v.2.1.
|
||||
* Avoid using blocking calls when initializing the trash can to speed up startup.
|
||||
* Updated Russian translation Removed ru_RU file
|
||||
* Create DnD menu with supported actions only
|
||||
* make createAction public so can be hidden from view
|
||||
* Adds Runtime and Devel install COMPONENT
|
||||
* Quote everything that could break due to whitespaces
|
||||
* Use CMake GenerateExportHeader to generate ABI header file
|
||||
* Use no exceptions also with Clang
|
||||
* Uses CMAKE_VISIBILITY and CMAKE_CXX_VISIBILITY flags
|
||||
* Use QString() instead of ""
|
||||
* Fix duplicated mtp mounts in the side pane by adding workarounds for potential bugs of gvfs.
|
||||
* Replace g_io_scheduler which is deprecated with QThread in Fm::PathEdit. * Fix compiler warnings.
|
||||
* Adds .gitignore
|
||||
* Makes Release the default BUILD_TYPE
|
||||
* Adds INSTALL package configuration file
|
||||
* Removes XCB from the dependencies
|
||||
* Adds target_include_directories() for the INSTALL_INTERFACE.
|
||||
* Removes CMAKE_CURRENT_BINARY_DIR usage
|
||||
* Removes QTX_INCLUDE_DIRS
|
||||
* Removes Qt Creator .user file
|
||||
* Updates libraries dependencies
|
||||
* Adds REQUIRED_PACKAGE_VERSION variables
|
||||
* Adds generation of TARGETS CMake file
|
||||
* Creates and installs an package version file
|
||||
* Renames and moves LIBRARY_NAME variable to the top CMakeLists
|
||||
* Removes sub-project
|
||||
* Rename the library to libfm-qt and fix installed header files.
|
||||
* Split pcmanfm-qt into two parts and move libfm-qt to its own repository.
|
||||
* Update French translation
|
||||
* Italian translation updates
|
||||
* Fix a crash triggered when unmounting a volume from the side pane.
|
||||
* Avoid duplicated volumes and mounts in the side panel. (This fixes the mtp:// problem.)
|
||||
* Fix missing null pointer check in Fm::Path::parent() and use nullptr instead of NULL in path.h.
|
||||
* Code cleanup, «using» directive only if necessary
|
||||
* Upgrade of pcmanfm-qt to C++11
|
||||
* hu translations updated
|
||||
* Fixed several problems with item selection and alignment
|
||||
* Remove unnecessary qDebug traces
|
||||
* Update translations.
|
||||
* The signal QAbstractItemView::iconSizeChanged is only available after Qt 5.5. Add workarounds for older Qt versions.
|
||||
* Add more null pointer checks in the thumbnail loader to avoid crashes caused by older versions of libfm.
|
||||
* Update translations
|
||||
* Avoid the column resizing tricks for the detailed list view when the view contains no columns.
|
||||
* Improve the column width computation for the detailed view
|
||||
* PlacesView: activate on click on the second column
|
||||
* SidePane: reduce size of button's column width
|
||||
* Added a filterbar + Handle virtually hidden files
|
||||
* Russian translation update
|
||||
* Update cs_CZ translation with the strings provided by petrbal in pull request #218.
|
||||
* Allow adding or removing paths in the file search dialog. Fix bugs in searching for documents.
|
||||
* Try to implement file searching by using the new Fm::FileSearchDialog class.
|
||||
* Fix a incorrecy free() in fm_search_free() API.
|
||||
* Add Fm::Path::take() API to take the ownership of a raw FmPath pointer.
|
||||
* Add class Fm::FileSearchDialog used to show a dialog for searching files.
|
||||
* Add FmSearch API which is used to build a search:// URI. (implemented in C and might become part of libfm later).
|
||||
* Fix #195 - Crash when rightclick on item in trash.
|
||||
* Add a null check for FmFileInfo in Fm::ProxyFolderModel::lessThan(). This closes #205.
|
||||
* Fix (workaround) for right-click crash in placesview.
|
||||
* Russian translation: update
|
||||
* Italian translation: add desktop entry files, adjust TS files
|
||||
* placesview: middle-click correct item to activate (fix of segfault)
|
||||
* Check for null pointers.
|
||||
* Select the folder from where we have gone up.
|
||||
* Paste into folder from its context menu.
|
||||
* libfm-qt: updated german translation
|
||||
* libfm-qt: lupdated translation files
|
||||
* Add Greek (el) translation
|
||||
* added support for mouse back/forward buttons
|
||||
* Update German translation
|
||||
* Add new signal prepareFileMenu() to Fm::SidePane and Fm::DirTree so there's a chance to customize the file menu before its shown.
|
||||
* Port some missing config options from the gtk+ version of pcmanfm.
|
||||
* Also show hidden dirs in the directory tree when the "Show Hidden" option in the menu is turned on.
|
||||
* Fix #190 - Column layout is not always updated.
|
||||
* Create New menu actions, context menu in tree side pane, #163.
|
||||
* Store side pane mode setting, #157.
|
||||
* Fixes an translation regression
|
||||
* Updates translations
|
||||
* Uses LXQt lxqt_translate_ts() to handle translations
|
||||
* Fix lxde/lxqt#447 - missing actions in Places' context menus
|
||||
* Remove trailing whitespaces
|
||||
* polishing German translation
|
||||
* Add menu items and shortcuts for new folder and blank file, fixes #163.
|
||||
* Display folders first when active and sort order descending, fixes #179.
|
||||
* Avoid space wasted by incorrect decoration in detailed view columns, fixes #177.
|
||||
* Avoid flickering column header while resizing manually, fixes #176.
|
||||
* Hungarian translation
|
||||
* Fix #627 - long startup time. (This blocking is caused by checking the availability of "network:///".)
|
||||
* Enable text selection in file properties dialog
|
||||
* Fix some memory leaks reported by valgrind.
|
||||
* Fix warnings reported by cppcheck.
|
||||
* Fix warnings reported by scan-build.
|
||||
* Sort indicators in detailed view, store column and order in settings, fixes #109.
|
||||
* Fix lxde/lxqt#512 - pcmanfm-qt: cannot delete to trash.
|
||||
* Polish translations added
|
||||
* Use 'user-trash' icon for 'Move to Trash'
|
||||
* The "Custom" option in the application chooser combo box inside file properties dialog is broken. Fix by preventing recursive signal handler invocation.
|
||||
* The file property dialog does not show correct default applications. Fix a bug in AppChooserComboBox::setMimeType() that does incorrect app comparison.
|
||||
* When converting an UID or GID to its name, show the number as string when the user or group does not exists.
|
||||
* Add more null checks.
|
||||
* Portuguese update
|
||||
* Add very basic "remaining time" display to the progress dialog. Fix lxde/lxqt#463 - Progress dialog of pcmanfm-qt does not show remaining time.
|
||||
* Fix lxde/pcmanfm-qt#120 - Foucs "Rename" button when file name changed.
|
||||
* Remove unnecessary '\n' charactor from the translated strings.
|
||||
* Fix translations (the newly added string comes from the translation of libfm).
|
||||
* Improve trash can handling: provide an option to delete the files if moving to trashcan fails.
|
||||
* Fix broken filenames of translation files.
|
||||
* More migration to Qt5 new signal/slot syntax for better type safety & speed.
|
||||
* Migrade to new Qt5 signal/slot syntax for better type safety and speed.
|
||||
* Fix the broken sorting option.
|
||||
* Fix lxde/lxqt#448 - PCmanFM-QT renaming place bookmarks does nothing.
|
||||
* Support linguistic sorting of file names. This fixes #105.
|
||||
* Update the folder model & view after files are changed.
|
||||
* Open folders in new tabs by middle clicking on items in the side pane.
|
||||
* Portuguese update
|
||||
* Fix a crash of the context menu of places view caused by change of items.
|
||||
* Save the result of "Edit bookmarks" to gtk+3 bookmarks file instead of the deprecated ~/.gtkbookmarks file. This fixes bug #112 partially.
|
||||
* Add spanish translations
|
||||
* Update Japanese translation
|
||||
* Add German translation
|
||||
* add Japanese translation
|
||||
* Implement "UnTrash" for files in trash:/// and close lxde/lxqt#136.
|
||||
* Add Russian translation
|
||||
* Drop Qt4 support in code
|
||||
* Clean up CMakeLists.txt and drop Qt4 support
|
||||
* New files added from LXDE Pootle server based on templates
|
||||
* Commit from LXDE Pootle server by user Julius22.: 1007 of 1008 strings translated (2 need review).
|
||||
* Commit from LXDE Pootle server by user Julius22.: 709 of 1008 strings translated (2 need review).
|
||||
* Commit from LXDE Pootle server by user mbouzada.: 364 of 364 strings translated (0 need review).
|
||||
* New files added from LXDE Pootle server based on templates
|
||||
* Add cs_CZ translation for libfm-qt.
|
||||
* Commit from LXDE Pootle server by user dforsi.: 364 of 364 strings translated (0 need review).
|
||||
* Commit from LXDE Pootle server by user dforsi.: 358 of 364 strings translated (0 need review).
|
||||
* Bump package version number and library soversion to prepare for 0.2 release.
|
||||
* Fix #85 - Scrolling doesn't work in compact view.
|
||||
* Hide UI elements that are not usable and disable trash can when gvfs is not available. * Add new API Fm::isUriSchemeSupported().
|
||||
* Avoid showing the popup menu when moving desktop items.
|
||||
* Improve handling of file selection and fixing FolderView::selectAll(), which is reported in #45. Delay the handling of selectionChanged() signal to avoid too frequent UI updates.
|
||||
* Little adjustment for the grid of the folder view to decrease unnecessary margins.
|
||||
* Use a new way to optimize the size of filename display based on current view mode and font size. This also fixes lxde/lxde-qt #198 - PCmanFM-qt incorrectly displays 256x256 Thumbnails.
|
||||
* Fully support single click activation and auto-selection with associated options added to the preference dialog.
|
||||
* Add single click and auto-selection on hover support to Fm::FolderView.
|
||||
* New files added from LXDE Pootle server based on templates
|
||||
* New files added from LXDE Pootle server based on templates
|
||||
* Improve update of translations to avoid unnecessary regeneration of ts files.
|
||||
* Improve handling of fallback icons. This closes bug #57.
|
||||
* Translations are lost accidentally in a previous commit. Restore them all.
|
||||
* Fix a crash in Fm::PlacesModel when gvfs is not available. This closes bug #35 - Ctrl+W closes all windows.
|
||||
* Do not detect filename extension and select the whole filename by default while renaming directories. This closes bug #71 - Don't try to detect extensions on directories. * API changed: Fm::renameFile() now accepect FmFileInfo as its first parameter.
|
||||
* Fix bug #80 - make execute in context menu doesn't do change permissions.
|
||||
* Revert "fixed selection issue #45" This patch breaks copying files by DND in icon view mode and moving desktop icons.
|
||||
* Use qss instead of QPalette to set the background color of ColorButton. This fixed bug #192 of lxde-qt.
|
||||
* Rename the library from libfm-qt to libfm-qt5 when built with Qt5.
|
||||
* fixed selection issue #45
|
||||
* Fix middle click position calculation in detailed view mode
|
||||
* Fix crash when context menu is requested but selection is empty
|
||||
* Activate view items only if clicked with left mouse button
|
||||
* Do not emit activated signal when keyboard modifiers are on.
|
||||
* Splits the checks for needed libraries
|
||||
* Removes duplicated include_directories() entry
|
||||
* Make sure clang compiler does not complain
|
||||
* Install pkgconfig file of libfm-qt to correct location in FreeBSD
|
||||
* Fix missing return values in several methods.
|
||||
* Avoid endless popups of error dialogs when there are errors launching files.
|
||||
* Save thumbnails as png files correctly.
|
||||
* Remember custom positions for desktop icons and fix #29.
|
||||
* Add template support to the folder context menus and fix #39.
|
||||
* Show "owner" in the detailed list view mode. * Fix a crash when switching to detailed list mode in qt5.
|
||||
* Use xcb to set EWMH window type hint to the desktop window in Qt5. * Some more cleanup for the CMakeList.txt files
|
||||
* Add initial support for Qt5.
|
||||
* Try to fix #36 again.
|
||||
* Fix a seg fault caused by the widget being deleted during glib signal handling.
|
||||
* Code cleanup, removing unnecessary header inclusion to speed up compilation.
|
||||
* Avoid further handling of MountOperation in the gio finished callback if the object is deleted.
|
||||
* Use modeless dialogs for app chooser and error reporting in Fm::FileLauncher and Fm::FileMenu.
|
||||
* Add an small emblem for symlinks (using icon name "emblem-symbolic-link"). Fix bug #27.
|
||||
* Add missing file to git.
|
||||
* Move internal implementation details to private headers which are not installed to the system.
|
||||
* Implement Fm::AppChooserDialog and Fm::AppMenuView classes. * Add <Open with...>/<Other Applications> menu item to Fm::FileMenu. * Add custom app to Fm::AppChooserComboBox.
|
||||
* Add Fm::AppChooserComboBox and use it in Fm::FilePropsDialog.
|
||||
* Redesign Fm::FileLauncher APIs to make it more usable. * Add Fm::FileMenu::setFileLauncher() and Fm::FolderView::setFileLauncher() APIs. * Move PCManFM::View::onFileClick() and other popup menu handling to Fm::FolderView.
|
||||
* Improve Fm::FileLaucher to make it easy to derive subclasses. * Implement a special dialog for opening executable files (Fix bug #13 - it does not launch executables)
|
||||
* Fix bug #28 - Tash can icon does not refresh when the trash can changes its empty/full status
|
||||
* Load autocompletion list for Fm::PathEdit only when the widget has the keyboard focus to avoid unnecessary I/O.
|
||||
* Add proper popup menu items for selected folders and fix #20 and #19. * Some code refactors, adding openFolders() and openFolderInTerminal() to Application class.
|
||||
* Fix #25 - Amount of items in the folder is not refreshed when the folder content changes. * Update status bar text properly on switching tab pages, selection changes, and folder content changes.
|
||||
* Fix the broken compiler definitions caused by previous commit.
|
||||
* Fix bug #22 - Do not select file extension by default on file rename. * Avoid installing private headers (*_p.h files)
|
||||
* Setup pcmanfm-qt to support optional Custom Actions Menubar detects if libfm was built with vala or not if so a fm-actions.h will exist and support will be compiled in if not, will still compile with no actions menu
|
||||
* Allow installation path configuration with standard CMake X_INSTALL_DIR
|
||||
* Support reordering bookmark items in the places view with DND.
|
||||
* Support adding bookmarks to the places view using drag and drop
|
||||
* Preparing for implementing dnd for places view.
|
||||
* Improve the usability of icon view mode, fixing github bug #24.
|
||||
* Fix crashes caused by invalid pointer access.
|
||||
* Switch current dir of the folder view correctly with dir tree view in the side pane.
|
||||
* Finish chdir operation for Fm::DirTreeView.
|
||||
* Support hiding hidden folders from DirTreeModel.
|
||||
* Move some methods from DirTreeModel to DirTreeModelItem and fix some row updating problems.
|
||||
* Implement dynamic folder loading/unloading when expanding or collapsing dir tree nodes. * Enable horizontal scrollbar of dir tree view.
|
||||
* Move some code from Fm::DirTreeModel to Fm::DirTreeModelItem.
|
||||
* Partially implement Fm::DirTreeView and Fm::DirTreeModel. (not finished yet)
|
||||
* Fix an invalid pointer
|
||||
* Implment different modes for Fm::SidePane, matching libfm-qtk design. * Add basic skeleton for dir tree view/model.
|
||||
* Fix the cosmetic defect introduced by the eject buttons in the places view.
|
||||
* Add eject buttons to mounted volumes in the places side pane.
|
||||
* Add a wrapper class Fm::Path for FmPath C struct.
|
||||
* Initialize icon_ member of PlacesModelItem correctly.
|
||||
* Fix fallback icon when a platform plugin is abscent. * Make Fm::IconTheme::checkUpdate() a static function.
|
||||
* Remove xsettings support. Use a cleaner way to detect config changes by monitor StyleChange events. * Add Fm::IconTheme::changed() signal which is emitted when the icon theme name is changed. * Replace nested Fm::PlacesModel::Item and related classes with their own separate toplevel classes.
|
||||
* Fix the icon for files of unknown mime types, again.
|
||||
* Fix the icon for files of unknown mime types.
|
||||
* Add DES-EMA custom actions to the popup menus.
|
||||
* Make it safe to create multiple Fm::LibFmQt objects and only initialize libfm once.
|
||||
* Fix incorrect export symbols and use GNUInstallDirs for installation destination
|
||||
* Use the latest libfm thumbnailer APIs.
|
||||
* Fix #3614873 - Thumbnails in icon view shown upside down for some jpegs.
|
||||
* Adopt recent changes of libfm. FmIcon is essentially identical to GIcon now.
|
||||
* Add a UI file for application chooser dialog.
|
||||
* Correctly handle display names of folders in path entry auto-completion.
|
||||
* Add a global header and add proper definition for LIBFM_QT_API macro.
|
||||
* Add "Empty trash" and fix a memory leak.
|
||||
* Fix memory leaks for bookmarks. Fix the broken "Network" item in places.
|
||||
* Reduce memory usage: Paint the folder items with our own code instead of using a dirty hacks duplicating pixmaps.
|
||||
* Reduce of size of QPixmapCache in the hope of decreasing memory usage.
|
||||
* Add fallback icons for places item "applications" and "network".
|
||||
* Add class Fm::CachedFolderModel, a convinient way to share Fm::FolderModel objects and reduce memory usage.
|
||||
* Resize the columns of detailed list view when items are inserted or removed.
|
||||
* Optimize column widths in detailed list mode when the view is resized.
|
||||
* Only show thumbnails for the first column in detailed list mode.
|
||||
* Use new "automoc" feature of cmake 2.8.6 and remove cumbersome #include "*.moc" code.
|
||||
* Trivial fix.
|
||||
* Add additional custom filter support to ProxyFolderModel.
|
||||
* Fix some memory leaks.
|
||||
* Fix some compiler errors and update translations.
|
||||
* Support the latest libfm trunk. Remove -fpermissive compiler flag and fix compiler errors/warnings.
|
||||
* Adopt new libfm thumbnail APIs.
|
||||
* Add soname 0.0.0 for libfm-qt, preparing for 0.1 release.
|
||||
* Fix crashes caused by incorrect deletion of dialog objects.
|
||||
* Enable thumbnail related settings.
|
||||
* Update zh_TW translations and translation templates.
|
||||
* Add Portuguese translation (pt).
|
||||
* Add Lithuanian translation (lt_LT).
|
||||
* Adopt the latest thumbnail API in libfm (thumbnail branch) to speed up loading.
|
||||
* Workardound incorrect thumbnail painting caused by bug of QStyledItemDelegate. :-(
|
||||
* Fix a crash caused by accessing data for invalid model index.
|
||||
* Fix a crash caused by accessing data for invalid model index.
|
||||
* Add basic thumbnail support (need the latest thumbnail branch of libfm).
|
||||
* Add archiver integration for file context menus.
|
||||
* Add archiver integration to file context menus.
|
||||
* Add mnemonics for menu items. Make confirm dialog before delete and trash can optional.
|
||||
* Update side pane according to current dir. Little fix.
|
||||
* Implement "Open in Terminal" and "Open as Root".
|
||||
* Implement "Auto Run" for newly inserted removable devices.
|
||||
* Add "Edit Bookmarks" dialog.
|
||||
* Implement "Invert Selection". Little fix of UI, add a Tool menu to main window.
|
||||
* Implement "Create New" menu in the folder popup menu.
|
||||
* Modify make rules for translations. Avoid deleting generated ts files when "make clean". Fix a small error in zh_TW translation.
|
||||
* Add auto-completion to path entry bar.
|
||||
* Rename Fm::Application to Fm::LibFmQt to decrease confusion. Set required Qt version to 4.6.
|
||||
* Load translation files correctly for pcmanfm-qt and libfm-qt.
|
||||
* Add basic skeleton for i18n (using Qt QTranslator & Qt Linguist).
|
||||
* Add separate CMakeLists.txt files for pcmanfm and libfm-qt. Hide more implementation details from libfm-qt headers.
|
||||
* Fix copyright notice in all source files.
|
||||
* Install a pkgconfig file for libfm-qt for use in other projects.
|
||||
* Fix a memory error caused by incorrect array size. Fix incorrect spacing of icons.
|
||||
* Finish chown and chmod supports.
|
||||
* Try to add file opermission settings UI.
|
||||
* Implement very basic drag and drop support.
|
||||
* Supress the incorrect default dnd handling of QListView.
|
||||
* Try to implement Dnd.
|
||||
* Finish desktop preferences.
|
||||
* Improve desktop preferences and apply settings (partially done).
|
||||
* Add desktop preferences dialog.
|
||||
* Apply side pane icon size correctly. Add basic skeleton for archiver integration.
|
||||
* Set shortcuts for frequently used menu options. Implement "rename file" support. Hide tabs when there is only one tab left (optional).
|
||||
* Delete windows properly when they're closed with setAttribute(Qt::WA_DeleteOnClose); Apply settings to windows after clicking OK in the preference dialog.
|
||||
* Improve preferences dialog. Change base class of SidePane to QWidget.
|
||||
* Sync the state of folder popup menu and main menu bar.
|
||||
* Implement sort options for main window.
|
||||
* Fix file sorting options for Fm::FolderMenu.
|
||||
* Correctly implement browse history and fix crashes.
|
||||
* Add very simple browse history (back/forward) handling.
|
||||
* Apply gcc visiblility attributes to export less symbols.
|
||||
* Correctly handle file rename/overwrite during file operations.
|
||||
* Exclude unnecessary files from CPack.
|
||||
* Improve folder popup menu.
|
||||
* Add folder popup menu. Some UI polishing.
|
||||
* Fix a little crash.
|
||||
* Fix crashes when turning off desktop manager.
|
||||
* Show popup menu for blank area of folders.
|
||||
* Do some refactor to make Fm::FolderView cleaner. Add PCManFM::View to do file manager-specific operations.
|
||||
* Move files for libfm-qt and pcmanfm-qt to separate subdirs.
|
89
CMakeLists.txt
Normal file
89
CMakeLists.txt
Normal file
@ -0,0 +1,89 @@
|
||||
cmake_minimum_required(VERSION 3.0.2)
|
||||
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_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.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")
|
||||
|
||||
set(REQUIRED_QT_VERSION "5.2")
|
||||
set(REQUIRED_LIBFM_VERSION "1.2.0")
|
||||
set(REQUIRED_LIBMENUCACHE_VERSION "0.4.0")
|
||||
set(REQUIRED_LXQT_BUILD_TOOLS_VERSION "0.4.0")
|
||||
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
|
||||
find_package(Qt5Widgets "${REQUIRED_QT_VERSION}" REQUIRED)
|
||||
find_package(Qt5LinguistTools "${REQUIRED_QT_VERSION}" REQUIRED)
|
||||
find_package(Qt5X11Extras "${REQUIRED_QT_VERSION}" REQUIRED)
|
||||
|
||||
find_package(lxqt-build-tools "${REQUIRED_LXQT_BUILD_TOOLS_VERSION}" REQUIRED)
|
||||
find_package(Fm "${REQUIRED_LIBFM_VERSION}" REQUIRED)
|
||||
find_package(MenuCache "${REQUIRED_LIBMENUCACHE_VERSION}" REQUIRED)
|
||||
find_package(Exif REQUIRED)
|
||||
find_package(XCB REQUIRED)
|
||||
|
||||
message(STATUS "Building ${PROJECT_NAME} with Qt ${Qt5Core_VERSION}")
|
||||
|
||||
option(UPDATE_TRANSLATIONS "Update source translation translations/*.ts files" OFF)
|
||||
include(GNUInstallDirs)
|
||||
include(GenerateExportHeader)
|
||||
include(CMakePackageConfigHelpers)
|
||||
include(LXQtTranslateTs)
|
||||
include(LXQtTranslateDesktop)
|
||||
include(LXQtCompilerSettings NO_POLICY_SCOPE)
|
||||
|
||||
set(CMAKE_AUTOMOC TRUE)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_BINARY_DIR}/${LIBFM_QT_LIBRARY_NAME}-config-version.cmake"
|
||||
VERSION ${LIBFM_QT_LIB_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_BINARY_DIR}/${LIBFM_QT_LIBRARY_NAME}-config-version.cmake"
|
||||
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${LIBFM_QT_LIBRARY_NAME}"
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
add_subdirectory(src)
|
||||
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
|
||||
option(BUILD_DOCUMENTATION "Use Doxygen to create the HTML based API documentation" OFF)
|
||||
if(BUILD_DOCUMENTATION)
|
||||
find_package(Doxygen REQUIRED)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" @ONLY)
|
||||
add_custom_target(doc ALL
|
||||
${DOXYGEN_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
COMMENT "Generating API documentation with Doxygen" VERBATIM
|
||||
)
|
||||
install(DIRECTORY
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/docs"
|
||||
DESTINATION "${CMAKE_INSTALL_DOCDIR}"
|
||||
COMPONENT Devel
|
||||
)
|
||||
endif()
|
1890
Doxyfile.in
Normal file
1890
Doxyfile.in
Normal file
File diff suppressed because it is too large
Load Diff
458
LICENSE
Normal file
458
LICENSE
Normal file
@ -0,0 +1,458 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
27
README.md
Normal file
27
README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# libfm-qt
|
||||
|
||||
## 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 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.
|
||||
|
||||
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.
|
||||
|
||||
### 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.
|
||||
|
||||
## Development
|
||||
|
||||
Issues should go to the tracker of PCManFM-Qt at https://github.com/lxde/pcmanfm-qt/issues.
|
41
cmake/fm-qt-config.cmake.in
Normal file
41
cmake/fm-qt-config.cmake.in
Normal file
@ -0,0 +1,41 @@
|
||||
#=============================================================================
|
||||
# Copyright 2015 Luís Pereira <luis.artur.pereira@gmail.com>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. The name of the author may not be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#=============================================================================
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
if (CMAKE_VERSION VERSION_LESS 3.0.2)
|
||||
message(FATAL_ERROR \"fm-qt requires at least CMake version 3.0.2\")
|
||||
endif()
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
if (NOT TARGET @LIBFM_QT_LIBRARY_NAME@)
|
||||
if (POLICY CMP0024)
|
||||
cmake_policy(SET CMP0024 NEW)
|
||||
endif()
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@LIBFM_QT_LIBRARY_NAME@-targets.cmake")
|
||||
endif()
|
10
data/CMakeLists.txt
Normal file
10
data/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
install(FILES
|
||||
"archivers.list"
|
||||
"terminals.list"
|
||||
DESTINATION "${CMAKE_INSTALL_DATADIR}/libfm-qt"
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"libfm-qt-mimetypes.xml"
|
||||
DESTINATION "${CMAKE_INSTALL_DATADIR}/mime/packages"
|
||||
)
|
35
data/archivers.list
Normal file
35
data/archivers.list
Normal file
@ -0,0 +1,35 @@
|
||||
[file-roller]
|
||||
create=file-roller --add %U
|
||||
extract=file-roller --extract %U
|
||||
extract_to=file-roller --extract-to %d %U
|
||||
mime_types=application/x-7z-compressed;application/x-7z-compressed-tar;application/x-ace;application/x-alz;application/x-ar;application/x-arj;application/x-bzip;application/x-bzip-compressed-tar;application/x-bzip1;application/x-bzip1-compressed-tar;application/x-cabinet;application/x-cbr;application/x-cbz;application/x-cd-image;application/x-compress;application/x-compressed-tar;application/x-cpio;application/x-deb;application/x-ear;application/x-ms-dos-executable;application/x-gtar;application/x-gzip;application/x-gzpostscript;application/x-java-archive;application/x-lha;application/x-lhz;application/x-lzip;application/x-lzip-compressed-tar;application/x-lzma;application/x-lzma-compressed-tar;application/x-lzop;application/x-lzop-compressed-tar;application/x-rar;application/x-rar-compressed;application/vnd.rar;application/x-rpm;application/x-rzip;application/x-tar;application/x-tarz;application/x-stuffit;application/x-war;application/x-xz;application/x-xz-compressed-tar;application/x-zip;application/x-zip-compressed;application/x-zoo;application/zip;multipart/x-zip;
|
||||
supports_uris=true
|
||||
|
||||
[xarchiver]
|
||||
create=xarchiver --add-to %F
|
||||
extract=xarchiver --extract %F
|
||||
extract_to=xarchiver --extract-to %d %F
|
||||
mime_types=application/x-arj;application/arj;application/x-bzip;application/x-bzip-compressed-tar;application/x-gzip;application/x-rar;application/x-rar-compressed;application/vnd.rar;application/x-tar;application/x-zip;application/x-zip-compressed;application/zip;multipart/x-zip;application/x-7z-compressed;application/x-compressed-tar;application/x-bzip2;application/x-bzip2-compressed-tar;application/x-lzma-compressed-tar;application/x-lzma;application/x-deb;application/deb;application/vnd.debian.binary-package;application/x-xz;application/x-xz-compressed-tar;application/x-rpm;application/x-source-rpm;application/x-lzop;application/x-lzop-compressed-tar;application/x-tzo;application/x-war;application/x-compress;application/x-tarz;application/x-java-archive;application/x-lha;application/x-lhz;
|
||||
|
||||
[squeeze]
|
||||
create=squeeze --new %F
|
||||
extract=squeeze --extract %F
|
||||
extract_to=squeeze --extract-to %d %F
|
||||
mime_types=application/x-bzip-compressed-tar;application/x-bzip2-compressed-tar;application/x-compressed-tar;application/x-tar;application/x-tarz;application/x-tzo;application/x-zip;application/x-zip-compressed;application/zip;application/x-rar;application/vnd.rar;application/x-gzip;application/x-bzip;application/x-lzop;application/x-compress;
|
||||
|
||||
[engrampa]
|
||||
create=engrampa --add %U
|
||||
extract=engrampa --extract %U
|
||||
extract_to=engrampa --extract-to %d %U
|
||||
mime_types=application/x-7z-compressed;application/x-7z-compressed-tar;application/x-ace;application/x-alz;application/x-ar;application/x-arj;application/x-bzip;application/x-bzip-compressed-tar;application/x-bzip1;application/x-bzip1-compressed-tar;application/x-cabinet;application/x-cbr;application/x-cbz;application/x-cd-image;application/x-compress;application/x-compressed-tar;application/x-cpio;application/x-deb;application/x-ear;application/x-ms-dos-executable;application/x-gtar;application/x-gzip;application/x-gzpostscript;application/x-java-archive;application/x-lha;application/x-lhz;application/x-lzip;application/x-lzip-compressed-tar;application/x-lzma;application/x-lzma-compressed-tar;application/x-lzop;application/x-lzop-compressed-tar;application/x-rar;application/x-rar-compressed;application/vnd.rar;application/x-rpm;application/x-rzip;application/x-tar;application/x-tarz;application/x-stuffit;application/x-war;application/x-xz;application/x-xz-compressed-tar;application/x-zip;application/x-zip-compressed;application/x-zoo;application/zip;multipart/x-zip;
|
||||
supports_uris=true
|
||||
|
||||
# The KDE archiver Ark
|
||||
# Here we use %F instead of %U since KDE programs do not know the URI provided by gvfs.
|
||||
# GIO will pass FUSE-based file paths to the KDE programs, which should still work.
|
||||
[ark]
|
||||
create=ark --add --dialog %F
|
||||
extract=ark --batch --dialog %F
|
||||
extract_to=ark --batch --destination %d %F
|
||||
mime_types=application/x-tar;application/x-compressed-tar;application/x-bzip-compressed-tar;application/x-tarz;application/x-xz-compressed-tar;application/x-lzma-compressed-tar;application/x-deb;application/x-cd-image;application/x-bcpio;application/x-cpio;application/x-cpio-compressed;application/x-sv4cpio;application/x-sv4crc;application/x-rpm;application/x-source-rpm;application/vnd.ms-cab-compressed;application/x-servicepack;application/x-rar;application/vnd.rar;application/x-7z-compressed;application/x-java-archive;application/zip;application/x-compress;application/x-gzip;application/x-bzip;application/x-bzip2;application/x-lzma;application/x-xz;application/lha;application/x-lha;application/maclha;
|
||||
supports_uris=true
|
52
data/libfm-qt-mimetypes.xml
Normal file
52
data/libfm-qt-mimetypes.xml
Normal file
@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Additional mime-types provided by libfm, adding some
|
||||
missing but frequently seen globs for some common mime-types.
|
||||
-->
|
||||
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
|
||||
|
||||
<mime-type type="text/plain">
|
||||
<glob pattern="*.ini"/>
|
||||
<glob pattern="*.inf"/>
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="application/x-ms-dos-executable">
|
||||
<glob pattern="*.com" />
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="application/x-ms-win-installer">
|
||||
<comment>Windows installer</comment>
|
||||
<comment xml:lang="zh_TW">Windows 安裝程式</comment>
|
||||
<glob pattern="*.msi" />
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="application/x-vbscript">
|
||||
<comment>MS VBScript</comment>
|
||||
<glob pattern="*.vbs" />
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="text/x-csharp">
|
||||
<comment xml:lang="en">C# source</comment>
|
||||
<comment xml:lang="zh_TW">C# 程式碼</comment>
|
||||
<glob pattern="*.cs"/>
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="application/x-desktop">
|
||||
<comment xml:lang="zh_TW">應用程式捷徑</comment>
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="application/x-sharedlib">
|
||||
<!--
|
||||
This pattern matching is not very accurate ,but the probability that
|
||||
a file is named like this and it's not a shared lib, is very low.
|
||||
-->
|
||||
<glob pattern="*.dll"/> <!-- Windows dll are shared libs, too -->
|
||||
<glob pattern="*.so.[0-9]" />
|
||||
<glob pattern="*.so.[0-9].*" />
|
||||
</mime-type>
|
||||
|
||||
<mime-type type="application/x-desktop">
|
||||
<glob pattern="*.directory"/>
|
||||
</mime-type>
|
||||
|
||||
</mime-info>
|
77
data/terminals.list
Normal file
77
data/terminals.list
Normal file
@ -0,0 +1,77 @@
|
||||
[xterm]
|
||||
open_arg=-e
|
||||
noclose_arg=-hold -e
|
||||
desktop_id=xterm.desktop
|
||||
|
||||
[uxterm]
|
||||
open_arg=-e
|
||||
noclose_arg=-hold -e
|
||||
|
||||
[lxterminal]
|
||||
open_arg=-e
|
||||
desktop_id=lxterminal.desktop
|
||||
|
||||
[konsole]
|
||||
open_arg=-e
|
||||
noclose_arg=--noclose -e
|
||||
desktop_id=konsole.desktop
|
||||
|
||||
[xfce4-terminal]
|
||||
open_arg=-x
|
||||
noclose_arg=--hold -x
|
||||
desktop_id=xfce4-terminal.desktop
|
||||
|
||||
[terminator]
|
||||
open_arg=-x
|
||||
desktop_id=terminator.desktop
|
||||
|
||||
[rxvt]
|
||||
open_arg=-e
|
||||
|
||||
[urxvt]
|
||||
open_arg=-e
|
||||
noclose_arg=-hold -e
|
||||
desktop_id=rxvt-unicode.desktop
|
||||
|
||||
[eterm]
|
||||
open_arg=-e
|
||||
noclose_arg=--pause -e
|
||||
desktop_id=eterm.desktop
|
||||
|
||||
[gnome-terminal]
|
||||
open_arg=-x
|
||||
desktop_id=gnome-terminal.desktop
|
||||
|
||||
[wterm]
|
||||
open_arg=-e
|
||||
|
||||
[roxterm]
|
||||
open_arg=-e
|
||||
desktop_id=roxterm.desktop
|
||||
|
||||
[sakura]
|
||||
open_arg=-e
|
||||
desktop_id=sakura.desktop
|
||||
|
||||
[qterminal]
|
||||
open_arg=-e
|
||||
desktop_id=qterminal.desktop
|
||||
|
||||
[lilyterm]
|
||||
open_arg=-e
|
||||
noclose_arg=--hold -e
|
||||
desktop_id=lilyterm.desktop
|
||||
|
||||
[urxvtc]
|
||||
open_arg=-e
|
||||
noclose_arg=-hold -e
|
||||
|
||||
[terminology]
|
||||
open_arg=-e
|
||||
noclose_arg=--hold -e
|
||||
desktop_id=terminology.desktop
|
||||
|
||||
[termite]
|
||||
open_arg=-e
|
||||
noclose_arg=--hold -e
|
||||
desktop_id=termite.desktop
|
490
debian/changelog
vendored
490
debian/changelog
vendored
@ -1,483 +1,3 @@
|
||||
libfm-qt (2.1.0-0ubuntu7) plucky; urgency=medium
|
||||
|
||||
* Update Standards-Version to 4.7.1, no changes needed.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Fri, 21 Feb 2025 16:22:38 -0600
|
||||
|
||||
libfm-qt (2.1.0-0ubuntu6) plucky; urgency=medium
|
||||
|
||||
* Add a Provides: libfm-qt6 line, to make it easier to track and use in
|
||||
reverse dependencies.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Sun, 16 Feb 2025 18:59:48 -0600
|
||||
|
||||
libfm-qt (2.1.0-0ubuntu5) plucky; urgency=medium
|
||||
|
||||
* No-change rebuild for Qt 6.8.2.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Mon, 03 Feb 2025 11:33:27 -0600
|
||||
|
||||
libfm-qt (2.1.0-0ubuntu4) plucky; urgency=medium
|
||||
|
||||
* No-change rebuild for lxqt-build-tools C++17 -> C++20.
|
||||
* Update symbols from build logs.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Fri, 03 Jan 2025 03:53:09 -0600
|
||||
|
||||
libfm-qt (2.1.0-0ubuntu3) plucky; urgency=medium
|
||||
|
||||
* No-change rebuild for Qt 6.8.1.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Wed, 04 Dec 2024 14:32:17 -0600
|
||||
|
||||
libfm-qt (2.1.0-0ubuntu2) plucky; urgency=medium
|
||||
|
||||
* No-change rebuild for Qt 6.8.0.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Wed, 04 Dec 2024 14:32:17 -0600
|
||||
|
||||
libfm-qt (2.1.0-0ubuntu1) plucky; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
- Bump build dependencies.
|
||||
- Update symbols from amd64 build logs.
|
||||
* Add new build dependencies from the CMake output.
|
||||
* Make the package Lintian-clean.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Fri, 15 Nov 2024 15:31:59 -0600
|
||||
|
||||
libfm-qt (2.0.2-0ubuntu3) plucky; urgency=medium
|
||||
|
||||
* No-change rebuild against Qt 6.7.2 private-abi.
|
||||
|
||||
-- Rik Mills <rikmills@kde.org> Thu, 17 Oct 2024 17:52:48 +0100
|
||||
|
||||
libfm-qt (2.0.2-0ubuntu2) oracular; urgency=medium
|
||||
|
||||
* Fix symbols file.
|
||||
|
||||
-- Aaron Rainbolt <arraybolt3@ubuntu.com> Sun, 25 Aug 2024 22:22:47 -0500
|
||||
|
||||
libfm-qt (2.0.2-0ubuntu1) oracular; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
* Bump Standards-Version to 4.7.0, no changes necessary.
|
||||
* Update copyright file.
|
||||
* Bump build deps.
|
||||
* Change binary package name to libfm-qt6.
|
||||
* Update symbols file.
|
||||
|
||||
-- Aaron Rainbolt <arraybolt3@ubuntu.com> Thu, 15 Aug 2024 16:06:46 -0500
|
||||
|
||||
libfm-qt (1.4.0-0ubuntu1) noble; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
* Reorganized and fixed copyright file.
|
||||
* Added lxqt-menu-data as a build dependency.
|
||||
* Bumped soname to libfm-qt14.
|
||||
* Updated symbols file.
|
||||
|
||||
-- Aaron Rainbolt <arraybolt3@gmail.com> Sun, 05 Nov 2023 17:15:40 -0600
|
||||
|
||||
libfm-qt (1.3.0-0ubuntu2) mantic; urgency=medium
|
||||
|
||||
* Update symbols from build logs.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Fri, 11 Aug 2023 10:16:27 -0500
|
||||
|
||||
libfm-qt (1.3.0-0ubuntu1) mantic; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
- Bump build dependency accordingly.
|
||||
* Rename binary package for SONAME change, update symbols accordingly.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Wed, 26 Jul 2023 16:25:53 -0500
|
||||
|
||||
libfm-qt (1.2.1-0ubuntu1) lunar; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
* Updated copyright file.
|
||||
* Bumped Standards-Version to 4.6.2, no changes necessary.
|
||||
* Updated symbols file.
|
||||
* Ran wrap-and-sort.
|
||||
|
||||
-- Aaron Rainbolt <arraybolt3@ubuntu.com> Thu, 26 Jan 2023 20:18:59 -0600
|
||||
|
||||
libfm-qt (1.2.0-0ubuntu3) lunar; urgency=medium
|
||||
|
||||
* No-change rebuild against Qt 5.15.8.
|
||||
|
||||
-- Dmitry Shachnev <mitya57@ubuntu.com> Sat, 14 Jan 2023 10:19:25 +0300
|
||||
|
||||
libfm-qt (1.2.0-0ubuntu2) lunar; urgency=medium
|
||||
|
||||
* No-change rebuild against Qt 5.15.7.
|
||||
|
||||
-- Dmitry Shachnev <mitya57@ubuntu.com> Sat, 10 Dec 2022 12:50:58 +0300
|
||||
|
||||
libfm-qt (1.2.0-0ubuntu1) lunar; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
* Fix the watch file, for real this time.
|
||||
* Lubuntuify the package slightly, to make debhelper happy.
|
||||
* Remove reverse-applicable upstream patches.
|
||||
* Bump build dependencies to the latest upstream LXQt versions.
|
||||
* Rename libfm-qt11 -> libfm-qt12 and add necessary Breaks/Replaces.
|
||||
* Update symbols from amd64 build logs.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Wed, 16 Nov 2022 19:21:46 -0600
|
||||
|
||||
libfm-qt (1.1.0-3ubuntu1) kinetic; urgency=medium
|
||||
|
||||
* Revert the previous upload.
|
||||
* Make Breaks/Replaces stricter for libfm-qt11 on libfm-qt-common due to the
|
||||
nature of the reverted upload.
|
||||
|
||||
-- Simon Quigley <tsimonq2@ubuntu.com> Mon, 10 Oct 2022 11:06:03 -0500
|
||||
|
||||
libfm-qt (1.1.0-2ubuntu1) kinetic; urgency=medium
|
||||
|
||||
* Update debian/libfm-qt11.symbols for GCC 12.
|
||||
|
||||
-- Dmitry Shachnev <mitya57@ubuntu.com> Sat, 13 Aug 2022 21:37:11 +0300
|
||||
|
||||
libfm-qt (1.1.0-2) experimental; urgency=medium
|
||||
|
||||
* Upstreamed compatible changes from Ubuntu, modifying the following files:
|
||||
- control
|
||||
- copyright
|
||||
- libfm-qt11.symbols
|
||||
- libfm-qt-common.install
|
||||
- libfm-qt-dev.install
|
||||
- rules
|
||||
- upstream/*
|
||||
- patches directory
|
||||
|
||||
-- Aaron Rainbolt <arraybolt3@gmail.com> Thu, 14 Jul 2022 15:45:41 -0500
|
||||
|
||||
libfm-qt (1.1.0-1) experimental; urgency=medium
|
||||
|
||||
* debian/changelog: fix a typo.
|
||||
* New upstream version 1.0.0
|
||||
* Add debian/salsa-ci.yml file.
|
||||
* Update debian/upstream/signing-key.asc.
|
||||
* New upstream version 1.1.0
|
||||
* debian/control: build-deps on lxqt-build-tools (>=0.11.0~).
|
||||
* Bump to libfm-qt11 to match soname changes.
|
||||
* debian/libfm-qt11.shlibs: Added, restrict to latest upstream version.
|
||||
* Drop C++ symbols according to
|
||||
https://wiki.debian.org/UsingSymbolsFiles.
|
||||
|
||||
-- Andrew Lee (李健秋) <ajqlee@debian.org> Tue, 12 Jul 2022 22:33:35 +0800
|
||||
|
||||
libfm-qt (0.16.0-3) unstable; urgency=high
|
||||
|
||||
* Update symbols for armel, armhf, i386, mipsel and s390x to fix FTBFS.
|
||||
|
||||
-- Andrew Lee (李健秋) <ajqlee@debian.org> Sat, 09 Jan 2021 03:15:46 +0800
|
||||
|
||||
libfm-qt (0.16.0-2) unstable; urgency=medium
|
||||
|
||||
* Source only upload.
|
||||
|
||||
-- Andrew Lee (李健秋) <ajqlee@debian.org> Wed, 06 Jan 2021 21:31:49 +0800
|
||||
|
||||
libfm-qt (0.16.0-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release. (Closes: #978219)
|
||||
* Drop fixed upstream patches.
|
||||
* Bump to libfm-qt8 to match soname changes.
|
||||
* Refresh debian/libfm-qt8.symbols file.
|
||||
|
||||
-- Andrew Lee (李健秋) <ajqlee@debian.org> Wed, 06 Jan 2021 04:30:38 +0800
|
||||
|
||||
libfm-qt (0.14.1-12.2) unstable; urgency=medium
|
||||
|
||||
* Non-maintainer upload.
|
||||
* Update symbols files from buildds’ logs to fix FTBFS on armel.
|
||||
|
||||
-- Dmitry Shachnev <mitya57@debian.org> Fri, 23 Oct 2020 20:28:57 +0300
|
||||
|
||||
libfm-qt (0.14.1-12.1) unstable; urgency=medium
|
||||
|
||||
* Non-maintainer upload.
|
||||
* Update symbols files for GCC 10 (closes: #957436).
|
||||
|
||||
-- Dmitry Shachnev <mitya57@debian.org> Sat, 17 Oct 2020 20:11:39 +0300
|
||||
|
||||
libfm-qt (0.14.1-12) unstable; urgency=medium
|
||||
|
||||
* Fixed symbol QRegEx -> QRegularExpression
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Tue, 22 Oct 2019 23:52:39 +0200
|
||||
|
||||
libfm-qt (0.14.1-11) unstable; urgency=medium
|
||||
|
||||
* Bumped Standards to 4.4.1, no changes needed
|
||||
* Switched to gbp, gbp.conf added
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sat, 05 Oct 2019 12:56:09 +0200
|
||||
|
||||
libfm-qt (0.14.1-10) unstable; urgency=medium
|
||||
|
||||
* Fixed symbols for gcc 9.2.1 (Closes: #925742)
|
||||
* Bumped Standards to 4.4.0, no changes needed
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sat, 17 Aug 2019 13:00:08 +0200
|
||||
|
||||
libfm-qt (0.14.1-9) unstable; urgency=medium
|
||||
|
||||
* Added upstream patch workaround-missed-file-monitoring.patch
|
||||
(Closes: #926803)
|
||||
* Added two new symbols - internal use only
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sat, 08 Jun 2019 16:39:11 +0200
|
||||
|
||||
libfm-qt (0.14.1-8) unstable; urgency=medium
|
||||
|
||||
* Removed the wrongly introduced build dependency on lxqt-qtplugin
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sat, 27 Apr 2019 18:40:25 +0200
|
||||
|
||||
libfm-qt (0.14.1-7) unstable; urgency=medium
|
||||
|
||||
* Workaround for GLib's recursive moving error (Closes: #927708)
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sat, 27 Apr 2019 10:50:16 +0200
|
||||
|
||||
libfm-qt (0.14.1-6) unstable; urgency=medium
|
||||
|
||||
* Fixed ignored creation-deletion sequences (Closes: #927707)
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sun, 21 Apr 2019 20:50:23 +0200
|
||||
|
||||
libfm-qt (0.14.1-5) unstable; urgency=medium
|
||||
|
||||
* Fixed license for vfs-search.c and vfs-menu.c - was forgotten upstream.
|
||||
* Fixed d/copyright that way.
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Thu, 18 Apr 2019 20:52:30 +0200
|
||||
|
||||
libfm-qt (0.14.1-4) unstable; urgency=medium
|
||||
|
||||
* Fix recursive copy to smb shares (and possible other remote filesystems)
|
||||
(Closes: #926626)
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Mon, 08 Apr 2019 19:03:43 +0200
|
||||
|
||||
libfm-qt (0.14.1-3) unstable; urgency=medium
|
||||
|
||||
* Fixed the remaining symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Fri, 01 Mar 2019 01:34:43 +0100
|
||||
|
||||
libfm-qt (0.14.1-2) unstable; urgency=medium
|
||||
|
||||
* Fixed new and optional symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Tue, 26 Feb 2019 22:07:40 +0100
|
||||
|
||||
libfm-qt (0.14.1-1) unstable; urgency=medium
|
||||
|
||||
* Cherry-picking upstream release 0.14.1.
|
||||
* Fixed some symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sun, 24 Feb 2019 19:31:01 +0100
|
||||
|
||||
libfm-qt (0.14.0-3) unstable; urgency=medium
|
||||
|
||||
* Switched to unstable
|
||||
* Fixed some more symbols
|
||||
* Ported back some upstream patches
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Tue, 05 Feb 2019 20:11:09 +0100
|
||||
|
||||
libfm-qt (0.14.0-2) experimental; urgency=medium
|
||||
|
||||
* Fixed a bunch of symbols for misc. architectures.
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sat, 02 Feb 2019 17:46:02 +0100
|
||||
|
||||
libfm-qt (0.14.0-1) experimental; urgency=medium
|
||||
|
||||
* Cherry-picking upstream release 0.14.0.
|
||||
* Switched to experimental
|
||||
* Bumped Standards to 4.3.0, no changes needed
|
||||
* Dropped d/compat, use debhelper-compat = 12, no changes needed
|
||||
* Fixed years in d/copyright
|
||||
* Bumped minimum version lxqt-build-tools (>= 0.6.0~)
|
||||
* Removed obsolete PULL_TRANSLATIONS= OFF from dh_auto_configure
|
||||
* Removed obsolete build dependency libfm-dev
|
||||
* Added Build-Depends-Package field to symbols
|
||||
* Added l10n-package, moved from lxqt-l10n
|
||||
* Fixed symbols file for 0.14.0
|
||||
* Added d/upstream/metadata
|
||||
* Added new files do debian/copyright
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Wed, 30 Jan 2019 20:32:59 +0100
|
||||
|
||||
libfm-qt (0.13.1-10) unstable; urgency=medium
|
||||
|
||||
* Added Build-Depends-Package: libfm-qt-dev to libfm-qt-dev.symbols
|
||||
* Bumped Standards to 4.2.1
|
||||
* Fixed the first symbols (Closes: #916158)
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Fri, 28 Dec 2018 16:41:09 +0100
|
||||
|
||||
libfm-qt (0.13.1-9) unstable; urgency=medium
|
||||
|
||||
* Fixed armel and riscv symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Fri, 31 Aug 2018 19:35:35 +0200
|
||||
|
||||
libfm-qt (0.13.1-8) unstable; urgency=medium
|
||||
|
||||
* Fixed missed blank in symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Fri, 31 Aug 2018 00:51:51 +0200
|
||||
|
||||
libfm-qt (0.13.1-7) unstable; urgency=medium
|
||||
|
||||
* Fixed another bunch of symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Thu, 30 Aug 2018 22:36:25 +0200
|
||||
|
||||
libfm-qt (0.13.1-6) unstable; urgency=medium
|
||||
|
||||
* Fixed a bunch of symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Tue, 31 Jul 2018 18:15:12 +0200
|
||||
|
||||
libfm-qt (0.13.1-5) unstable; urgency=medium
|
||||
|
||||
* Switch to unstable
|
||||
* Another round of symbol fixes
|
||||
* Bumped Standards to 4.1.5, no changes needed
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sat, 07 Jul 2018 13:05:18 +0200
|
||||
|
||||
libfm-qt (0.13.1-4) experimental; urgency=medium
|
||||
|
||||
* Fixed some more symbols in architectures
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Fri, 29 Jun 2018 19:34:00 +0200
|
||||
|
||||
libfm-qt (0.13.1-3) experimental; urgency=medium
|
||||
|
||||
* Fixed some powerpcspe symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Thu, 28 Jun 2018 01:22:45 +0200
|
||||
|
||||
libfm-qt (0.13.1-2) experimental; urgency=medium
|
||||
|
||||
* Added dependency libglib2.0-bin (Closes: #902053)
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sat, 23 Jun 2018 14:45:18 +0200
|
||||
|
||||
libfm-qt (0.13.1-1) experimental; urgency=medium
|
||||
|
||||
* Cherry-picking upstream release 0.13.1.
|
||||
* Fixed some symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sun, 03 Jun 2018 01:30:24 +0200
|
||||
|
||||
libfm-qt (0.13.0-2) experimental; urgency=medium
|
||||
|
||||
* Fixed the first big bunch of symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Mon, 28 May 2018 19:36:36 +0200
|
||||
|
||||
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
|
||||
* Messed around with symbols (gcc7/gcc8, removals, new symbols)
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Wed, 23 May 2018 23:02:00 +0200
|
||||
|
||||
libfm-qt (0.12.0-17) unstable; urgency=medium
|
||||
|
||||
* Fixed symbols for ia64
|
||||
* Fixed watch file to lxqt
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sun, 29 Apr 2018 02:31:23 +0200
|
||||
|
||||
libfm-qt (0.12.0-16) unstable; urgency=medium
|
||||
|
||||
* fix some typos in symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Mon, 23 Apr 2018 02:07:46 +0200
|
||||
|
||||
libfm-qt (0.12.0-15) unstable; urgency=medium
|
||||
|
||||
* Added missed symbols (Closes: #896514)
|
||||
* Bumped debhelper build dependency to 11~
|
||||
* Bumped compat to 11
|
||||
* Bumped Standards to 4.1.4
|
||||
* Fixed VCS fields for salsa
|
||||
* Fixed upstream homepage
|
||||
* Fixed years and source in copyright
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sun, 22 Apr 2018 22:39:57 +0200
|
||||
|
||||
libfm-qt (0.12.0-14) unstable; urgency=medium
|
||||
|
||||
* Bumped Standards to 4.1.2, no changes needed
|
||||
* Removed branch from VCS fields
|
||||
* Removed debian/gbp.conf
|
||||
* Removed trailing whitespaces
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Wed, 13 Dec 2017 20:08:02 +0100
|
||||
|
||||
libfm-qt (0.12.0-13) unstable; urgency=medium
|
||||
|
||||
* Transition to unstable
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Mon, 04 Dec 2017 18:41:41 +0100
|
||||
|
||||
libfm-qt (0.12.0-12) experimental; urgency=medium
|
||||
|
||||
* finally fixed the symbols for the very most architectures
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Fri, 01 Dec 2017 20:51:49 +0100
|
||||
|
||||
libfm-qt (0.12.0-11) experimental; urgency=medium
|
||||
|
||||
* added new symbol to mips64el
|
||||
* added and fixed a bunch of armel symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sun, 05 Nov 2017 00:27:13 +0100
|
||||
|
||||
libfm-qt (0.12.0-10) experimental; urgency=medium
|
||||
|
||||
* fixed symbols for mips64el
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sat, 04 Nov 2017 17:55:04 +0100
|
||||
|
||||
libfm-qt (0.12.0-9) experimental; urgency=medium
|
||||
|
||||
* next round of symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sun, 29 Oct 2017 02:59:13 +0200
|
||||
|
||||
libfm-qt (0.12.0-8) experimental; urgency=medium
|
||||
|
||||
* introduced two optional symbols
|
||||
* fixed sh4 symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Sat, 28 Oct 2017 17:45:54 +0200
|
||||
|
||||
libfm-qt (0.12.0-7) experimental; urgency=medium
|
||||
|
||||
* Some more symbol fixes for armel, armhf, mips, mipsel. hppa, m68k,
|
||||
ppc64 and sh4
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Thu, 26 Oct 2017 16:30:32 +0200
|
||||
|
||||
libfm-qt (0.12.0-6) experimental; urgency=medium
|
||||
|
||||
* Fixed symbols for arm64, ppc64el, s390x, hppa, powerpc, ppc64 and sparc64
|
||||
@ -486,14 +6,14 @@ libfm-qt (0.12.0-6) experimental; urgency=medium
|
||||
|
||||
libfm-qt (0.12.0-5) experimental; urgency=medium
|
||||
|
||||
* Symbol fixes for i386 - this will introduce regressions for other
|
||||
* Symbol fixes for i386 - this will introduce regressions for other
|
||||
architectures
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Thu, 26 Oct 2017 00:57:28 +0200
|
||||
|
||||
libfm-qt (0.12.0-4) experimental; urgency=medium
|
||||
|
||||
* Symbol fixes for armel, mipsel, hurd-i386, x32
|
||||
* Symbol fixes for armel, mipsel, hurd-i386, x32
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Tue, 24 Oct 2017 23:49:03 +0200
|
||||
|
||||
@ -549,7 +69,7 @@ libfm-qt (0.11.2-1) unstable; urgency=medium
|
||||
libfm-qt (0.11.1-3) unstable; urgency=medium
|
||||
|
||||
* Backported a upstream fix for Icon Emblem Support.
|
||||
* Added needed Symbols
|
||||
* Added needed Symbols
|
||||
|
||||
-- Alf Gaida <agaida@siduction.org> Thu, 20 Oct 2016 18:00:38 +0200
|
||||
|
||||
@ -566,7 +86,7 @@ libfm-qt (0.11.1-2) unstable; urgency=medium
|
||||
* Added build dependency libqt5svg5-dev
|
||||
* Added Recommends libfm-qt-l10n
|
||||
* Exported LC_ALL=C.UTF-8 - define language settings for reproducible
|
||||
builds
|
||||
builds
|
||||
* Hard override translation control
|
||||
* Use DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
* Fixed copyright Format field, using https
|
||||
@ -617,7 +137,7 @@ libfm-qt (0.10.0+20151214-2) unstable; urgency=medium
|
||||
libfm-qt (0.10.0+20151214-1) unstable; urgency=medium
|
||||
|
||||
[ Alf Gaida ]
|
||||
* forked pcmanfm-qt debian/$foo
|
||||
* forked pcmanfm-qt debian/$foo
|
||||
* Imported upstream version 0.10.0+20151214.
|
||||
* Initial release (Closes: #808192)
|
||||
|
||||
|
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@ -0,0 +1 @@
|
||||
10
|
119
debian/control
vendored
119
debian/control
vendored
@ -1,73 +1,41 @@
|
||||
Source: libfm-qt
|
||||
Maintainer: Lubuntu Developers <lubuntu-devel@lists.ubuntu.com>
|
||||
Original-Maintainer: LXQt Packaging Team <pkg-lxqt-devel@lists.alioth.debian.org>
|
||||
Maintainer: LXQt Packaging Team <pkg-lxqt-devel@lists.alioth.debian.org>
|
||||
Uploaders: Alf Gaida <agaida@siduction.org>,
|
||||
ChangZhuo Chen (陳昌倬) <czchen@debian.org>,
|
||||
Andrew Lee (李健秋) <ajqlee@debian.org>,
|
||||
Shih-Yuan Lee (FourDollars) <fourdollars@gmail.com>,
|
||||
Simon Quigley <tsimonq2@debian.org>,
|
||||
Aaron Rainbolt <arraybolt3@gmail.com>
|
||||
Shih-Yuan Lee (FourDollars) <fourdollars@gmail.com>
|
||||
Section: x11
|
||||
Priority: optional
|
||||
Build-Depends: debhelper-compat (= 13),
|
||||
Build-Depends: debhelper (>= 10),
|
||||
libexif-dev,
|
||||
libfm-dev (>= 1.2.0),
|
||||
libglib2.0-dev,
|
||||
libkf5windowsystem-dev,
|
||||
libmenu-cache-dev,
|
||||
libqt5svg5-dev,
|
||||
libqt5x11extras5-dev,
|
||||
libx11-dev,
|
||||
libxcb-composite0-dev,
|
||||
libxcb-cursor-dev,
|
||||
libxcb-damage0-dev,
|
||||
libxcb-dpms0-dev,
|
||||
libxcb-dri2-0-dev,
|
||||
libxcb-dri3-dev,
|
||||
libxcb-ewmh-dev,
|
||||
libxcb-glx0-dev,
|
||||
libxcb-icccm4-dev,
|
||||
libxcb-image0-dev,
|
||||
libxcb-keysyms1-dev,
|
||||
libxcb-present-dev,
|
||||
libxcb-randr0-dev,
|
||||
libxcb-record0-dev,
|
||||
libxcb-render-util0-dev,
|
||||
libxcb-render0-dev,
|
||||
libxcb-res0-dev,
|
||||
libxcb-screensaver0-dev,
|
||||
libxcb-shape0-dev,
|
||||
libxcb-shm0-dev,
|
||||
libxcb-sync-dev,
|
||||
libxcb-util-dev,
|
||||
libxcb-xf86dri0-dev,
|
||||
libxcb-xfixes0-dev,
|
||||
libxcb-xinerama0-dev,
|
||||
libxcb-xinput-dev,
|
||||
libxcb-xkb-dev,
|
||||
libxcb-xtest0-dev,
|
||||
libxcb-xv0-dev,
|
||||
libxcb-xvmc0-dev,
|
||||
lxqt-build-tools (>= 2.1.0),
|
||||
lxqt-menu-data (>= 2.1.0),
|
||||
qt6-base-private-dev (>= 6.6.0),
|
||||
qt6-svg-dev (>= 6.6.0)
|
||||
Standards-Version: 4.7.1
|
||||
Vcs-Browser: https://git.lubuntu.me/Lubuntu/libfm-qt-packaging
|
||||
Vcs-Git: https://git.lubuntu.me/Lubuntu/libfm-qt-packaging.git
|
||||
Debian-Vcs-Browser: https://salsa.debian.org/lxqt-team/libfm-qt
|
||||
Debian-Vcs-Git: https://salsa.debian.org/lxqt-team/libfm-qt.git
|
||||
Homepage: https://github.com/lxqt/libfm-qt
|
||||
Rules-Requires-Root: no
|
||||
lxqt-build-tools (>= 0.4.0),
|
||||
qtbase5-private-dev
|
||||
Standards-Version: 4.1.1
|
||||
Vcs-Browser: https://anonscm.debian.org/cgit/pkg-lxqt/libfm-qt.git/?h=debian/experimental
|
||||
Vcs-Git: https://anonscm.debian.org/git/pkg-lxqt/libfm-qt.git -b debian/experimental
|
||||
Homepage: https://github.com/lxde/libfm-qt
|
||||
|
||||
Package: libfm-qt6-15
|
||||
Provides: libfm-qt6 (= 15)
|
||||
Replaces: libfm-qt14, libfm-qt6-14
|
||||
Breaks: libfm-qt14, libfm-qt6-14
|
||||
Package: libfm-qt3
|
||||
Provides: libfm-qt
|
||||
Conflicts: libfm-qt
|
||||
Replaces: libfm-qt5-2,
|
||||
libfm-qt2
|
||||
Breaks: libfm-qt5-2,
|
||||
libfm-qt2
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: libs
|
||||
Depends: libfm-qt6-common,
|
||||
libglib2.0-bin,
|
||||
shared-mime-info,
|
||||
${misc:Depends},
|
||||
${shlibs:Depends}
|
||||
Recommends: libfm-qt6-l10n
|
||||
Depends: ${misc:Depends},
|
||||
${shlibs:Depends},
|
||||
shared-mime-info
|
||||
Recommends: libfm-qt-l10n
|
||||
Pre-Depends: ${misc:Pre-Depends}
|
||||
Description: file management support for pcmanfm-qt
|
||||
Libfm-Qt is a companion library providing components to build desktop file
|
||||
@ -75,39 +43,18 @@ Description: file management support for pcmanfm-qt
|
||||
.
|
||||
This package contains helper library libfm-qt for pcmanfm-qt.
|
||||
|
||||
Package: libfm-qt6-common
|
||||
Architecture: all
|
||||
Multi-Arch: foreign
|
||||
Section: libs
|
||||
Depends: ${misc:Depends}
|
||||
Breaks: libfm-qt14
|
||||
Replaces: libfm-qt14
|
||||
Description: Common files for libfm-qt
|
||||
Libfm-Qt is a companion library providing components to build desktop file
|
||||
managers.
|
||||
.
|
||||
This package contains common files for libfm-qt.
|
||||
|
||||
Package: libfm-qt6-dev
|
||||
Package: libfm-qt-dev
|
||||
Replaces: libfm-qt5-dev
|
||||
Breaks: libfm-qt5-dev
|
||||
Architecture: any
|
||||
Section: libdevel
|
||||
Depends: libexif-dev,
|
||||
libfm-qt6-15 (= ${binary:Version}),
|
||||
libmenu-cache-dev,
|
||||
${misc:Depends}
|
||||
Depends: ${misc:Depends},
|
||||
libexif-dev,
|
||||
libfm-dev (>= 1.2.0),
|
||||
libfm-qt3 (= ${binary:Version}),
|
||||
libmenu-cache-dev
|
||||
Description: file management support library for pcmanfm-qt (development files)
|
||||
Libfm-Qt is a companion library providing components to build desktop file
|
||||
managers.
|
||||
.
|
||||
This package contains development files for helper library libfm-qt.
|
||||
|
||||
Package: libfm-qt6-l10n
|
||||
Architecture: all
|
||||
Multi-Arch: foreign
|
||||
Section: localization
|
||||
Depends: qt6-translations-l10n, ${misc:Depends}
|
||||
Description: Language package for libfm-qt
|
||||
Libfm-Qt is a companion library providing components to build desktop file
|
||||
managers.
|
||||
.
|
||||
This package contains the l10n files needed by the libfm-qt.
|
||||
|
41
debian/copyright
vendored
41
debian/copyright
vendored
@ -1,15 +1,12 @@
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: libfm-qt
|
||||
Source: https://github.com/lxqt/libfm-qt
|
||||
Source: https://github.com/lxde/libfm-qt
|
||||
|
||||
Files: *
|
||||
Copyright: 2013-2024 LXQt team
|
||||
2009 Juergen Hoetzel <juergen@archlinux.org>
|
||||
2010 Shae Smittle <starfall87@gmail.com>
|
||||
Copyright: 2013-2017 LXQt team
|
||||
2013-2017 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
||||
2012-2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
|
||||
2014 Kuzma Shapran <kuzma.shapran@gmail.com>
|
||||
2016 Mamoru TASAKA <mtasaka@fedoraproject.org>
|
||||
2012-2016 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
|
||||
2009-2019 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
||||
License: LGPL-2.1+
|
||||
|
||||
Files: cmake/fm-qt-config.cmake.in
|
||||
@ -17,18 +14,10 @@ Copyright: 2015 Luís Pereira <luis.artur.pereira@gmail.com>
|
||||
License: BSD-3-Clause
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2015 Andrew Lee (李健秋) <ajqlee@debian.org>
|
||||
2014-2015 Wen Liao <wen.cf83@gmail.com>
|
||||
Copyright: 2014-2015 Wen Liao <wen.cf83@gmail.com>
|
||||
2014-2016 ChangZhuo Chen (陳昌倬) <czchen@debian.org>
|
||||
2019 Rik Mills <rikmills@kubuntu.org>
|
||||
2013-2019 Alf Gaida <agaida@siduction.org>
|
||||
2018-2019 Walter Lapchynski <wxl@ubuntu.com>
|
||||
2018-2019 Dan Simmons <monetaryabyss@protonmail.com>
|
||||
2020 Gianfranco Costamagna <locutusofborg@debian.org>
|
||||
2019-2021 Raman Sarda <ramansarda2000@gmail.com>
|
||||
2019-2021 apt-ghetto <apt-ghetto@protonmail.com>
|
||||
2018-2025 Simon Quigley <tsimonq2@ubuntu.com>
|
||||
2022-2024 Aaron Rainbolt <arraybolt3@gmail.com>
|
||||
2013-2017 Alf Gaida <agaida@siduction.org>
|
||||
2015 Andrew Lee (李健秋) <ajqlee@debian.org>
|
||||
License: LGPL-2.1+
|
||||
|
||||
License: BSD-3-Clause
|
||||
@ -45,15 +34,15 @@ License: BSD-3-Clause
|
||||
without specific prior written permission.
|
||||
.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE HOLDERS OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
License: LGPL-2.1+
|
||||
|
4
debian/gbp.conf
vendored
4
debian/gbp.conf
vendored
@ -2,5 +2,7 @@
|
||||
debian-branch = debian/sid
|
||||
upstream-branch = upstream/latest
|
||||
pristine-tar = True
|
||||
compression = xz
|
||||
|
||||
[import-orig]
|
||||
# Use git cherrypick -n upstream instead.
|
||||
merge = False
|
||||
|
8
debian/libfm-qt-dev.install
vendored
Normal file
8
debian/libfm-qt-dev.install
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
usr/include/libfm-qt/*.h
|
||||
usr/lib/*/*.so
|
||||
usr/lib/*/pkgconfig/*
|
||||
|
||||
usr/share/cmake/fm-qt/*.cmake
|
||||
|
||||
usr/include/libfm-qt/customactions/*.h
|
||||
usr/include/libfm-qt/core/*.h
|
4
debian/libfm-qt3.install
vendored
Normal file
4
debian/libfm-qt3.install
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
usr/lib/*/*.so.*
|
||||
usr/share/libfm-qt/terminals.list
|
||||
usr/share/libfm-qt/archivers.list
|
||||
usr/share/mime/packages/libfm-qt-mimetypes.xml
|
1351
debian/libfm-qt3.symbols
vendored
Normal file
1351
debian/libfm-qt3.symbols
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
debian/libfm-qt6-15.install
vendored
1
debian/libfm-qt6-15.install
vendored
@ -1 +0,0 @@
|
||||
usr/lib/*/*.so.*
|
1678
debian/libfm-qt6-15.symbols
vendored
1678
debian/libfm-qt6-15.symbols
vendored
File diff suppressed because it is too large
Load Diff
3
debian/libfm-qt6-common.install
vendored
3
debian/libfm-qt6-common.install
vendored
@ -1,3 +0,0 @@
|
||||
usr/share/libfm-qt6/archivers.list
|
||||
usr/share/libfm-qt6/terminals.list
|
||||
usr/share/mime/packages/libfm-qt6-mimetypes.xml
|
8
debian/libfm-qt6-dev.install
vendored
8
debian/libfm-qt6-dev.install
vendored
@ -1,8 +0,0 @@
|
||||
usr/include/libfm-qt6/*.h
|
||||
usr/include/libfm-qt6/core/*.h
|
||||
usr/include/libfm-qt6/core/legacy/*.h
|
||||
usr/include/libfm-qt6/core/vfs/*.h
|
||||
usr/include/libfm-qt6/customactions/*.h
|
||||
usr/lib/*/*.so
|
||||
usr/lib/*/pkgconfig/*
|
||||
usr/share/cmake/fm-qt6/*.cmake
|
1
debian/libfm-qt6-l10n.install
vendored
1
debian/libfm-qt6-l10n.install
vendored
@ -1 +0,0 @@
|
||||
usr/share/libfm-qt6/translations
|
@ -1,16 +0,0 @@
|
||||
Description: Fix metadata for trusting executables
|
||||
To conform with the Ubuntu standard use metadata::trusted instead of metadata::trust. This patch must be applied after any upstream version changes since they refuse to conform.
|
||||
Author: Dan Simmons <monetaryabyss@protonmail.com>
|
||||
Bug: https://bugs.launchpad.net/bugs/1813687
|
||||
Last-Update: 2019-01-29
|
||||
--- a/src/core/fileinfo.cpp
|
||||
+++ b/src/core/fileinfo.cpp
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "fileinfo_p.h"
|
||||
#include <gio/gio.h>
|
||||
|
||||
-#define METADATA_TRUST "metadata::trust"
|
||||
+#define METADATA_TRUST "metadata::trusted"
|
||||
|
||||
namespace Fm {
|
||||
|
1
debian/patches/series
vendored
1
debian/patches/series
vendored
@ -1 +0,0 @@
|
||||
fix-metadata-for-trusting-executables.patch
|
2
debian/rules
vendored
2
debian/rules
vendored
@ -2,6 +2,7 @@
|
||||
# export DH_VERBOSE = 1
|
||||
|
||||
export LC_ALL=C.UTF-8
|
||||
export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
|
||||
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||
|
||||
%:
|
||||
@ -12,5 +13,6 @@ override_dh_missing:
|
||||
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- \
|
||||
-DPULL_TRANSLATIONS=OFF \
|
||||
-DUPDATE_TRANSLATIONS=OFF \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
|
12
debian/salsa-ci.yml
vendored
12
debian/salsa-ci.yml
vendored
@ -1,12 +0,0 @@
|
||||
# For more information on what jobs are run see:
|
||||
# https://salsa.debian.org/salsa-ci-team/pipeline
|
||||
#
|
||||
# To enable the jobs, go to your repository (at salsa.debian.org)
|
||||
# and click over Settings > CI/CD > Expand (in General pipelines).
|
||||
# In "Custom CI config path" write debian/salsa-ci.yml and click
|
||||
# in "Save Changes". The CI tests will run after the next commit.
|
||||
---
|
||||
include:
|
||||
- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml
|
||||
- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml
|
||||
|
5
debian/source/lintian-overrides
vendored
5
debian/source/lintian-overrides
vendored
@ -1,5 +0,0 @@
|
||||
# This list will always be very long, it's okay
|
||||
libfm-qt source: very-long-line-length-in-source-file * > 512 [data/archivers.list:*]
|
||||
|
||||
# Long lines in the upstream changelog are okay
|
||||
libfm-qt source: very-long-line-length-in-source-file * > 512 [CHANGELOG:*]
|
5
debian/upstream/metadata
vendored
5
debian/upstream/metadata
vendored
@ -1,5 +0,0 @@
|
||||
Name: libfm-qt
|
||||
Bug-Database: https://github.com/lxqt/libfm-qt/issues
|
||||
Bug-Submit: https://github.com/lxqt/libfm-qt/issues/new
|
||||
Changelog: https://github.com/lxqt/libfm-qt/blob/master/CHANGELOG
|
||||
Repository: https://github.com/lxqt/libfm-qt
|
96
debian/upstream/signing-key.asc
vendored
96
debian/upstream/signing-key.asc
vendored
@ -1,52 +1,50 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBF6cxrwBEADfl3ydxNfLBbWGPesXty2baQgixZ3D6aCxadI2kX+aikmT8rd0
|
||||
ttDKN18cXV52Ssxnj0qhgf4hwnu/b0be6BzqSEyGM+UQR3X2CYpxrMakfW32Q18K
|
||||
X5ec0RPR2ucBq9G0r9t6FYC8FkJ4uQUU3xxrLW3z302S0Makjgzm8BV9WrFQ7oFF
|
||||
uJQj0BHbHYC4RyaZb2AfxY4Y92BPGTjtGekWqgw6vEXCCnvAbGYVQzvxZt3nw21/
|
||||
1YmV4g7xhGFQPbOf9v3ejFUJeJIGzuJf5NAh7kvfCdUBAGYH0gnj0GpOve4ftnaG
|
||||
sAId2CQwm3oYF4Tu7yBPTOBpkaKkNaT+UdwTyeKERuCZ9ocZWX++/YF9ItRkJ5mM
|
||||
zoP1GluWn2atNWpRh/K97gyAGgr2fSmrAA4d1JrVbMujZAHoHAOKwJKqX9jPziPZ
|
||||
BFHfhcIOzG3ZhXAuumHsd7uwfPBVt20g+G+cOjBghbSSu9EOtMkAZl1g3ybvZixu
|
||||
Jtxa5exZWEmU7vtytEb8eq9Dj5XcGoTDbErE2RpJ/20HPzhyRKg9RN4iGS+0OiHS
|
||||
oRbDi5IEOizvQjp2bsBmfa3rsoDSOqF2pevp+u8I56I6bU1GFpxxNC5IGvgo2Q79
|
||||
quz0oIk5hs3eLlUdEYsLGwR6pWJaJyf36vuDsq7iLrLyvHI5irAowO4r1QARAQAB
|
||||
tCVQZWRyYW0gUG91cmFuZyA8dHN1amFuMjAwMEBnbWFpbC5jb20+iQJOBBMBCAA4
|
||||
FiEEGd/fOleb1QnbtXLYvnkwB60i334FAl6cxrwCGwMFCwkIBwIGFQoJCAsCBBYC
|
||||
AwECHgECF4AACgkQvnkwB60i335f9RAAgRpn8gUa/l10UkVAnpM2Cz0MuNMwwCOq
|
||||
IfVnuZuPBtYYiTU5Su++/aPZe3fF5B4v61F+XjNi7qeVL2t52X3jZ/iIx9Syasb+
|
||||
vDAIfQ5t6lKXvOptWxf6vteOg6CHbXwpGHbPjUkUS2vQwRikjBnR0SnkrMoXtgSX
|
||||
amPFqsitNrOhEJfeDfo0NzKESZuliWrCFt2v8c5q18G8cCZAvPLBlGuwRl58cDep
|
||||
3EIibMI/9MUSJbKoiHlK+LcHtG7BQTNis/e7Pe1PkRmExfhxe1lNajtOx8FO72Tq
|
||||
B6zY6drippM9VaIc1M+zp9BRpsFu8whOmapCqlXHRgAK8xTdQRIGInQFqLWPOxSC
|
||||
f0B6N+EvQvgkyFQ1rW+u91OJBma46uKkhrwf+mDttVRncaIAkgE6e6pqm18yIPFk
|
||||
D42rt/yHcOl+2qkcJS3gPcg5UvlCzqOwg1rKZQIk+TcPuDx3r2UghDEYZN9X6vw3
|
||||
zCBufr7ygZNf4tkbnVARFWTR4GzyCseFkWgOVZL9DccAhs8NeMy1WLkUzB75adeR
|
||||
3LONmEL7xOI8FuknKY4e6EcWhmstNIDgXfRe0hwO0VBdW3unoZC/K2ZM/ZuZyMdK
|
||||
TFjvYJrNewmymKge68wo0054bGZn8oz17i2AosJz7kW+ITsxmxhVcpfl4bav9Neq
|
||||
RpQwhnhK9bC5Ag0EXpzGvAEQANbeRHFbpgQVIqV9WVOVnTj4FIqrTPTPKKa02vJA
|
||||
7tGpgFapgvjdxnMxJfV6wuwOBUUFLR7DrXlV8EVFAYc5qTIeSQXvJsWw6gQ3+f0D
|
||||
z13oGOhZPBIzIKnV/MZI/jhIio8kSPWAuM5hR2X9Hvw3/CLo+H+hZZ6cFYoCxrQS
|
||||
tTzcKMkdQizLLa+WNbqUSxg6I/P5k/smUDY9gKW7RtI5t/PupA3WTnsVD6CYWa3Q
|
||||
c1O/1mUgqT6nQ5N9KCPpjZQRT6D6eIMmePtS85z4PPeYMJxPsKRYWPGRxKhCSdZl
|
||||
/0wsC8aRtmwYT729e0ZgTAmUnj+rQp5hboF/ZPFjIoXR9G+0HnoY0a/nqVO4lUON
|
||||
AV25GnMFGVyiHHlbH/0gboywwnzEg8BZbk+Z/61oOzBIW09sfG8fn8bsbkpL+nHf
|
||||
Mi/Vauge6wSfw7I5AfSiwrSDNHmKVsu39koWV6JGxEeFr2MffF+CuaoJCNOr/ZII
|
||||
SYR5ku3Y/lMKyUH1Oas0RWzFrdRcInqYK90A0x083zP4V445MvCwbRPzQAkm9wOP
|
||||
kILLhE5FW+9/O0/9bpx4joJUDLV4d3hFZy7GSHKiZUs1QW6BV75JQKqoi+cVt+/L
|
||||
+o1S8CMNekjqdC2mWRosM3doo51zT/FWNzQA1QcoZP2hORJDfw66y+4wPq6o8y1W
|
||||
jR35ABEBAAGJAjYEGAEIACAWIQQZ3986V5vVCdu1cti+eTAHrSLffgUCXpzGvAIb
|
||||
DAAKCRC+eTAHrSLffgbJD/4qW5YOo/BayBhaUh2L7VP7JNlECb/2xNNOFKI1NjNr
|
||||
nOmgSJLzf74Uhmt5W+iVjmJBHrDceprIPkizmPrn90kIsPIMtHIDNxzUgKZHbnza
|
||||
j1vZyAeC+JV79X1hOVpprj1TJwy65lpxXNyYnGqeIOgyFokn9fOHXv8aMQwpNuUr
|
||||
bdUJ1C75jYrvwy/NR1DczIFFYgsbkDGDtjVBjyMc5JAgvUBz37/iVPJfWP6dKVnf
|
||||
abRnUVzHgvgK7bnab00SA1TiWvjHURGjo+5rnRtv8X/AgStc2Phjq68TMIgMn0F2
|
||||
kjUVvfQotNqzo9madNshvUDmsGtAzKh4e0dS1ear7u3nRp4Z7fqSrTEtXKNbEPwZ
|
||||
wdWrWmmQLacNQBSe/FtcMzGF6xIVr4lnrL0bFjqBdQpdTC7vns3QSKk8/GFiEfpv
|
||||
kzXrDbGV7jX2OWDjNHKcmXX2+E1CsNaJgS7zOgZw5jvbvlTLJUwyYNlM1VLI2OFW
|
||||
Oa86l8pqli+B7rpTbsAE9Ut8qUaWjm87oUNSJbaKgqNnMaE+b/8VJaEeWHgQJwsD
|
||||
bJSJ/O/vzlRtDjOJ1JDlMRLs7TnOFeUh5pgwyaJoidYbJEiGlMGJbI6BjwhDTBFO
|
||||
NLJtd3SsRjc7ICtGdCvej59IvCDTjxtkhx5okF03APi1aXpHQrE18/arFD7BpoGO
|
||||
sw==
|
||||
=gSIv
|
||||
mQINBFXQeMMBEACif4+9pTrC6uNmRng0ZbzLh7p3cazmbnp2YFgDQDJZ7ZNmebxy
|
||||
ngRuRhjGuDcFAL/37BwJnrBpfZFK9ljoH4Fo5Jm9cOELaTy7AIcEiV9dKMyrKF1E
|
||||
C76d8jHVuzuPbI92DkFdLZAdk+qjrrAy0x43PvUd+aaBGLcFs1ZMk7gOvElc2d95
|
||||
zWWSp5anjukmGbp+EsStnWJkF6VHj56qmklfYy5ioiVBOSpXo/RsACAcIlz8C8A1
|
||||
d4tNMiB2uF2OrUfrL8DD6m3nBqep+AYbIQrxMl9kUQH3I33e9kH/L+SHQyE6phS8
|
||||
Czq06WjV4TcJ9VWxm7hQCNLYSxhZYYr1AW45lS5+xmfBOq2qeLgvjbFxa8PPrsp6
|
||||
Bqgt8MjwUkXjU5IB7YulUBvFU2l0MJZWDBuNy0oNtCe1cU3JyIqLKjvzQQQ9eD5L
|
||||
o3Ul704TLHz0z+67Rxh05Mi4JvyFMjnooSJkNH8/7yXoBN0ZGOh1/5zMU1gK5bmP
|
||||
6hKgis2exSZNIS74mF6/PqGgcwk3PyI4T3keUQoNPj11M2EznLHxY19QZfQ5oMed
|
||||
8xOlHKjpcm8PYMB4gduNXlV7gI9h7UxuC5GuPiP2lmM6wUyHu48divxDk5UYgPEC
|
||||
xlPI2wHCNDsuy0EruCYIvrMSZfpYCCSrmXiOORBLO5qXkauILLkJarHqjQARAQAB
|
||||
tCBBbGYgR2FpZGEgPGFnYWlkYUBzaWR1Y3Rpb24ub3JnPokCOAQTAQIAIgUCVdB4
|
||||
wwIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQQsnI069epeOT2xAAgSHf
|
||||
41103cnElGf6TokPl4J6hdRPy2CUAjmBtMfr8eajYvGDGgnmsh9AGYGURjfFVCCf
|
||||
Ag+8b6nF3xg03UmgsuSO8H78HGv9kKzF9aHmLt+SXq3jUX+LnIkFHErZWjFAKdJr
|
||||
luu1j6ltxLe9PQljxZnugzMaUbW8eEPKvcriiDn3S4/DtikW/jpGA0MTY4ZWs9pZ
|
||||
L/6iRRH99L2X/cWO4sCgDXCTt4oK0f5OvwiuCoVOM+PYoIm31JICCKOlqamkCn7d
|
||||
2KH3nsy0v7tXgnrnb/zr8jVGsZLzUE51AFOzb5Ec74/2SAq8X4gbTppttLXEIooq
|
||||
nbepitW/PePkPY5gpfwHtFbl88qFnir+ABMefqRZkzeh0tsxJVLVHGP1KZykXpv7
|
||||
96A6Q1h7Zo9Ny7WwN5Xl02g35LVCaPyzd3A8A4315uMuP3iziq57UktKqh9d5S3t
|
||||
jfK7e9UfFQZBLfxn2sNPsjdYSNUQp/PXTTk/599h359WVuUIR866T8K7N7EEon3p
|
||||
qLItZljQ9Nmr/yGwKi9iQgi2LtZj5KUcF1zBLzZKf95FvoqSZqBXdFSjm+eYGaCH
|
||||
Q2IBnhyP92lEknSK9ystUJXmY69tQKBFqJxScwaS+7a/rfLKssQjSWxqk+SX4QeW
|
||||
e9z9FUpo71bq0Zkc/M9aOCoEEmhg4Ob/JWy08oC5Ag0EVdB4wwEQAKZDCc/C41y0
|
||||
omLFCAJybvHiFScM+jOpyGpQvceoviEhIT7h1br/pnSEMkgPQEDPWJGtKueg1/94
|
||||
sXTH24uefr3Y6JdZoBtprxl4JXUoOndgq1QH1xuUsy3/9YWU8Qboy9j8a8w0oCDE
|
||||
T8Z03KHCwqzD3K+44jhmhF+0eLoaaY8ohS8ziP+DcFKVHyatmS5yCCdjVrj6PxMp
|
||||
uy/y5SXT1kmiPdVAIzQlM5DlN6o46TV+BH0pPvVYjtwf31o0FckJxy5S1v0koCNB
|
||||
vX2b7tTDPKzn8G18eUVhGoUTZBUCp1gg36wJ0YY4xgZ9vI/xDCeHeAkyvGtaTAoy
|
||||
qP4rHoUO5KVRSDh7frSlrdbLGWHaQwOhcqoKd4qP/164wHPGkgHL1vztdOc7l1wx
|
||||
q3gMh2uwmJR0NRrw4WVuaIqL9lEbGBNijlmGsuqXfsMRhc/qoqgVDWvrcCtEoOwl
|
||||
TONGobW3jpCCjpa9SeGNjxuY6IVLn0lfX4hItNVY9sFA+H+yj4uBQ7zsmMUXafxt
|
||||
Yllm0f98yGNg5lnJg4bLOYu3IkpogUKNA3qkZ+6vRtwH70/bJGp7qdx/3G4W5dMX
|
||||
asd/rJjdELW+R/NVULAmK1ETSklaa3Z6vbTu8bN8gvP8pmMJ8f/U8+qzkuAqc201
|
||||
Z4O+s7ZsQfTiz5mm7zPGIYTnppDSno/rABEBAAGJAh8EGAECAAkFAlXQeMMCGwwA
|
||||
CgkQQsnI069epeMt0g/+JrwLhULD6NOxaLgxboh/KZkh/7ViU4cB+QPT8JIcWxkZ
|
||||
zj8uk85TUitEUzKmjp/ItCrhQE5WNNWbz/FBnAuLtaQuHhcHMA3Vu95UUCGi1vyZ
|
||||
ZRlS3YRM6S9BOzrjG7fGQJmO/RU3g6rb0TAwGFxDHj8t4JEDTc3zASG7wV/VTn06
|
||||
d8XIH9CZOw3kUuhkQ3OR/PEj1BCeCC+caC+tBjO0fgvDp8RV7NFQQ9kH8R3/xlWd
|
||||
6KMPtILE6fUft6LubWRGd1P5JBuzXivELolASajewbYtL/s87CCji3ngq0aT9raK
|
||||
m02wqFzNbX1iv+w2iqPQXq6pdRyxtJ8+Q8Z7zEBGJS5nkrYjsLTduZIjJHYHYH7f
|
||||
3/ydVjQ3z12iqHKElgaRI7RUmpNiNxVIr+TtuxzeC6G+CF++XNkUtJODvCmRaoJS
|
||||
waYsitz8+LSv3tawZJ0iQkKc9nerQMuBD+AzIr3i4NgXiEIN513esUtnKzeyIIsL
|
||||
ntUcBjXKuLCj8OZrZtexjq7edWWbN57/3ikyS2Z7y0i3O30qk5jmccSaS6kA7xTY
|
||||
WCDFzbN2v2y+vGu9KYn+2HtrP2BtNa8JTh3waNeLUTpn4GV4mMrsZjOy6vhhHb91
|
||||
1TKfI1gvjk7lE9xaWmcDjdI55dw3jIq8kK9SdgORGq9/S3g7KJNRjme+6GjqQfk=
|
||||
=h7ww
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
|
6
debian/watch
vendored
6
debian/watch
vendored
@ -1,5 +1,3 @@
|
||||
version=4
|
||||
opts="searchmode=plain, \
|
||||
pgpsigurlmangle=s/$/.asc/, \
|
||||
uversionmangle=s/(\d+\.\d+\.\d+).*/$1/" \
|
||||
https://api.github.com/repos/lxqt/@PACKAGE@/releases https:\/\/github.com\/lxqt\/@PACKAGE@\/releases\/download\/@ANY_VERSION@\/@PACKAGE@-@ANY_VERSION@.tar.xz
|
||||
opts="pgpsigurlmangle=s/$/.asc/" \
|
||||
https://github.com/lxde/libfm-qt/releases .*/libfm-qt-([\d\.]+).tar.xz
|
||||
|
269
src/CMakeLists.txt
Normal file
269
src/CMakeLists.txt
Normal file
@ -0,0 +1,269 @@
|
||||
set(libfm_core_SRCS
|
||||
# core data structures
|
||||
core/gobjectptr.h
|
||||
core/filepath.cpp
|
||||
core/iconinfo.cpp
|
||||
core/mimetype.cpp
|
||||
core/fileinfo.cpp
|
||||
core/folder.cpp
|
||||
core/filemonitor.cpp
|
||||
# i/o jobs
|
||||
core/job.cpp
|
||||
core/copyjob.cpp
|
||||
core/deletejob.cpp
|
||||
core/dirlistjob.cpp
|
||||
core/filechangeattrjob.cpp
|
||||
core/fileinfojob.cpp
|
||||
core/filelinkjob.cpp
|
||||
core/fileoperationjob.cpp
|
||||
core/filesysteminfojob.cpp
|
||||
core/job.cpp
|
||||
core/totalsizejob.cpp
|
||||
core/trashjob.cpp
|
||||
core/untrashjob.cpp
|
||||
core/thumbnailjob.cpp
|
||||
# extra desktop services
|
||||
core/bookmarks.cpp
|
||||
core/volumemanager.cpp
|
||||
core/userinfocache.cpp
|
||||
core/thumbnailer.cpp
|
||||
core/terminal.cpp
|
||||
# custom actions
|
||||
customactions/fileaction.cpp
|
||||
customactions/fileactionprofile.cpp
|
||||
customactions/fileactioncondition.cpp
|
||||
)
|
||||
|
||||
set(libfm_SRCS
|
||||
${libfm_core_SRCS}
|
||||
libfmqt.cpp
|
||||
bookmarkaction.cpp
|
||||
sidepane.cpp
|
||||
icontheme.cpp
|
||||
filelauncher.cpp
|
||||
foldermodel.cpp
|
||||
foldermodelitem.cpp
|
||||
cachedfoldermodel.cpp
|
||||
proxyfoldermodel.cpp
|
||||
folderview.cpp
|
||||
folderitemdelegate.cpp
|
||||
createnewmenu.cpp
|
||||
filemenu.cpp
|
||||
foldermenu.cpp
|
||||
filepropsdialog.cpp
|
||||
applaunchcontext.cpp
|
||||
placesview.cpp
|
||||
placesmodel.cpp
|
||||
placesmodelitem.cpp
|
||||
dirtreeview.cpp
|
||||
dirtreemodel.cpp
|
||||
dirtreemodelitem.cpp
|
||||
dnddest.cpp
|
||||
mountoperation.cpp
|
||||
mountoperationpassworddialog.cpp
|
||||
mountoperationquestiondialog.cpp
|
||||
fileoperation.cpp
|
||||
fileoperationdialog.cpp
|
||||
renamedialog.cpp
|
||||
pathedit.cpp
|
||||
pathbar.cpp
|
||||
colorbutton.cpp
|
||||
fontbutton.cpp
|
||||
browsehistory.cpp
|
||||
utilities.cpp
|
||||
dndactionmenu.cpp
|
||||
editbookmarksdialog.cpp
|
||||
execfiledialog.cpp
|
||||
appchoosercombobox.cpp
|
||||
appmenuview.cpp
|
||||
appchooserdialog.cpp
|
||||
filesearchdialog.cpp
|
||||
filedialog.cpp
|
||||
fm-search.c # might be moved to libfm later
|
||||
xdndworkaround.cpp
|
||||
)
|
||||
|
||||
set(libfm_UIS
|
||||
file-props.ui
|
||||
file-operation-dialog.ui
|
||||
rename-dialog.ui
|
||||
mount-operation-password.ui
|
||||
edit-bookmarks.ui
|
||||
exec-file.ui
|
||||
app-chooser-dialog.ui
|
||||
filesearch.ui
|
||||
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")
|
||||
|
||||
# add translation for libfm-qt
|
||||
lxqt_translate_ts(QM_FILES
|
||||
UPDATE_TRANSLATIONS ${UPDATE_TRANSLATIONS}
|
||||
SOURCES ${libfm_SRCS} ${libfm_UIS}
|
||||
INSTALL_DIR "${LIBFM_QT_DATA_DIR}/translations"
|
||||
PULL_TRANSLATIONS ${PULL_TRANSLATIONS}
|
||||
CLEAN_TRANSLATIONS ${CLEAN_TRANSLATIONS}
|
||||
TRANSLATIONS_REPO ${TRANSLATIONS_REPO}
|
||||
TRANSLATIONS_REFSPEC ${TRANSLATIONS_REFSPEC}
|
||||
)
|
||||
|
||||
add_library(${LIBFM_QT_LIBRARY_NAME} SHARED
|
||||
${libfm_SRCS}
|
||||
${libfm_UIS_H}
|
||||
${QM_FILES}
|
||||
)
|
||||
|
||||
install(EXPORT
|
||||
"${LIBFM_QT_LIBRARY_NAME}-targets"
|
||||
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${LIBFM_QT_LIBRARY_NAME}"
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
target_link_libraries(${LIBFM_QT_LIBRARY_NAME}
|
||||
Qt5::Widgets
|
||||
Qt5::X11Extras
|
||||
${FM_LIBRARIES}
|
||||
${MENUCACHE_LIBRARIES}
|
||||
${XCB_LIBRARIES}
|
||||
${EXIF_LIBRARIES}
|
||||
)
|
||||
|
||||
# set libtool soname
|
||||
set_target_properties(${LIBFM_QT_LIBRARY_NAME} PROPERTIES
|
||||
VERSION ${LIBFM_QT_LIB_VERSION}
|
||||
SOVERSION ${LIBFM_QT_LIB_SOVERSION}
|
||||
)
|
||||
|
||||
target_include_directories(${LIBFM_QT_LIBRARY_NAME}
|
||||
PRIVATE "${Qt5Gui_PRIVATE_INCLUDE_DIRS}"
|
||||
PUBLIC
|
||||
"${FM_INCLUDE_DIRS}"
|
||||
"${FM_INCLUDE_DIR}/libfm" # to workaround incorrect #include in fm-actions.
|
||||
"${MENUCACHE_INCLUDE_DIRS}"
|
||||
"${XCB_INCLUDE_DIRS}"
|
||||
"${EXIF_INCLUDE_DIRS}"
|
||||
INTERFACE
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
"$<BUILD_INTERFACE:${LIBFM_QT_INTREE_INCLUDE_DIR}>"
|
||||
)
|
||||
|
||||
target_compile_definitions(${LIBFM_QT_LIBRARY_NAME}
|
||||
PRIVATE "LIBFM_QT_DATA_DIR=\"${LIBFM_QT_DATA_DIR}\""
|
||||
PUBLIC "QT_NO_KEYWORDS"
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${LIBFM_QT_LIBRARY_NAME}_export.h"
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/libfm-qt"
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
# install include header files (FIXME: can we make this cleaner? should dir name be versioned?)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/libfm-qt"
|
||||
COMPONENT Devel
|
||||
FILES_MATCHING PATTERN "*.h"
|
||||
)
|
||||
|
||||
generate_export_header(${LIBFM_QT_LIBRARY_NAME}
|
||||
EXPORT_MACRO_NAME LIBFM_QT_API
|
||||
)
|
||||
|
||||
# InTree build
|
||||
file(COPY ${CMAKE_CURRENT_BINARY_DIR}/${LIBFM_QT_LIBRARY_NAME}_export.h
|
||||
DESTINATION "${LIBFM_QT_INTREE_INCLUDE_DIR}/libfm-qt"
|
||||
)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/
|
||||
DESTINATION "${LIBFM_QT_INTREE_INCLUDE_DIR}/libfm-qt"
|
||||
FILES_MATCHING PATTERN "*.h"
|
||||
)
|
||||
|
||||
configure_package_config_file(
|
||||
"${PROJECT_SOURCE_DIR}/cmake/fm-qt-config.cmake.in"
|
||||
"${CMAKE_BINARY_DIR}/${LIBFM_QT_LIBRARY_NAME}-config.cmake"
|
||||
INSTALL_DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${LIBFM_QT_LIBRARY_NAME}"
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_BINARY_DIR}/${LIBFM_QT_LIBRARY_NAME}-config.cmake"
|
||||
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${LIBFM_QT_LIBRARY_NAME}"
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
# FIXME: add libtool version to the lib (soname) later.
|
||||
# FIXME: only export public symbols
|
||||
|
||||
install(TARGETS ${LIBFM_QT_LIBRARY_NAME}
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
EXPORT "${LIBFM_QT_LIBRARY_NAME}-targets"
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
|
||||
PUBLIC_HEADER
|
||||
COMPONENT Runtime
|
||||
)
|
||||
|
||||
export(TARGETS ${LIBFM_QT_LIBRARY_NAME}
|
||||
FILE "${CMAKE_BINARY_DIR}/${LIBFM_QT_LIBRARY_NAME}-targets.cmake"
|
||||
EXPORT_LINK_INTERFACE_LIBRARIES
|
||||
)
|
||||
|
||||
# install a pkgconfig file for libfm-qt
|
||||
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
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/lib${LIBFM_QT_LIBRARY_NAME}.pc"
|
||||
DESTINATION libdata/pkgconfig
|
||||
COMPONENT Devel
|
||||
)
|
||||
else()
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/lib${LIBFM_QT_LIBRARY_NAME}.pc"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
|
||||
COMPONENT Devel
|
||||
)
|
||||
endif()
|
||||
|
||||
# prevent the generated files from being deleted during make cleaner
|
||||
set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM true)
|
||||
|
||||
|
||||
set(TEST_LIBRARIES
|
||||
Qt5::Core
|
||||
Qt5::Widgets
|
||||
${FM_LIBRARIES}
|
||||
${LIBFM_QT_LIBRARY_NAME}
|
||||
)
|
||||
# some simple test cases
|
||||
add_executable("test-folder"
|
||||
tests/test-folder.cpp
|
||||
)
|
||||
target_link_libraries("test-folder" ${TEST_LIBRARIES})
|
||||
|
||||
add_executable("test-folderview"
|
||||
tests/test-folderview.cpp
|
||||
)
|
||||
target_link_libraries("test-folderview" ${TEST_LIBRARIES})
|
||||
|
||||
add_executable("test-filedialog"
|
||||
tests/test-filedialog.cpp
|
||||
)
|
||||
target_link_libraries("test-filedialog" ${TEST_LIBRARIES})
|
||||
|
||||
add_executable("test-volumemanager"
|
||||
tests/test-volumemanager.cpp
|
||||
)
|
||||
target_link_libraries("test-volumemanager" ${TEST_LIBRARIES})
|
||||
|
||||
add_executable("test-placesview"
|
||||
tests/test-placesview.cpp
|
||||
)
|
||||
target_link_libraries("test-placesview" ${TEST_LIBRARIES})
|
||||
|
183
src/app-chooser-dialog.ui
Normal file
183
src/app-chooser-dialog.ui
Normal file
@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AppChooserDialog</class>
|
||||
<widget class="QDialog" name="AppChooserDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>432</width>
|
||||
<height>387</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Choose an Application</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="fileTypeHeader"/>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>1</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Installed Applications</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="Fm::AppMenuView" name="appMenuView"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Custom Command</string>
|
||||
</attribute>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Command line to execute:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLineEdit" name="cmdLine"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Application name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="appName"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string><b>These special codes can be used in the command line:</b>
|
||||
<ul>
|
||||
<li><b>%f</b>: Represents a single file name</li>
|
||||
<li><b>%F</b>: Represents multiple file names</li>
|
||||
<li><b>%u</b>: Represents a single URI of the file</li>
|
||||
<li><b>%U</b>: Represents multiple URIs</li>
|
||||
</ul></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="keepTermOpen">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Keep terminal window open after command execution</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="useTerminal">
|
||||
<property name="text">
|
||||
<string>Execute in terminal emulator</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="setDefault">
|
||||
<property name="text">
|
||||
<string>Set selected application as default action of this file type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Fm::AppMenuView</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>appmenuview.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>AppChooserDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>227</x>
|
||||
<y>359</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>AppChooserDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>295</x>
|
||||
<y>365</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>useTerminal</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>keepTermOpen</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>72</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>79</x>
|
||||
<y>282</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
142
src/appchoosercombobox.cpp
Normal file
142
src/appchoosercombobox.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (C) 2014 - 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 "appchoosercombobox.h"
|
||||
#include "icontheme.h"
|
||||
#include "appchooserdialog.h"
|
||||
#include "utilities.h"
|
||||
#include "core/iconinfo.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
AppChooserComboBox::AppChooserComboBox(QWidget* parent):
|
||||
QComboBox(parent),
|
||||
defaultAppIndex_(-1),
|
||||
prevIndex_(0),
|
||||
blockOnCurrentIndexChanged_(false) {
|
||||
|
||||
// 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
|
||||
connect((QComboBox*)this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &AppChooserComboBox::onCurrentIndexChanged);
|
||||
}
|
||||
|
||||
AppChooserComboBox::~AppChooserComboBox() {
|
||||
}
|
||||
|
||||
void AppChooserComboBox::setMimeType(std::shared_ptr<const Fm::MimeType> mimeType) {
|
||||
clear();
|
||||
defaultApp_.reset();
|
||||
appInfos_.clear();
|
||||
|
||||
mimeType_ = std::move(mimeType);
|
||||
if(mimeType_) {
|
||||
const char* typeName = mimeType_->name();
|
||||
defaultApp_ = Fm::GAppInfoPtr{g_app_info_get_default_for_type(typeName, FALSE), false};
|
||||
GList* appInfos_glist = g_app_info_get_all_for_type(typeName);
|
||||
int i = 0;
|
||||
for(GList* l = appInfos_glist; l; l = l->next, ++i) {
|
||||
Fm::GAppInfoPtr app{G_APP_INFO(l->data), false};
|
||||
GIcon* gicon = g_app_info_get_icon(app.get());
|
||||
addItem(gicon ? Fm::IconInfo::fromGIcon(gicon)->qicon(): QIcon(), g_app_info_get_name(app.get()));
|
||||
if(g_app_info_equal(app.get(), defaultApp_.get())) {
|
||||
defaultAppIndex_ = i;
|
||||
}
|
||||
appInfos_.push_back(std::move(app));
|
||||
}
|
||||
g_list_free(appInfos_glist);
|
||||
}
|
||||
// add "Other applications" item
|
||||
insertSeparator(count());
|
||||
addItem(tr("Customize"));
|
||||
if(defaultAppIndex_ != -1) {
|
||||
setCurrentIndex(defaultAppIndex_);
|
||||
}
|
||||
}
|
||||
|
||||
// returns the currently selected app.
|
||||
Fm::GAppInfoPtr AppChooserComboBox::selectedApp() const {
|
||||
int idx = currentIndex();
|
||||
return idx >= 0 ? appInfos_[idx] : Fm::GAppInfoPtr{};
|
||||
}
|
||||
|
||||
bool AppChooserComboBox::isChanged() const {
|
||||
return (defaultAppIndex_ != currentIndex());
|
||||
}
|
||||
|
||||
void AppChooserComboBox::onCurrentIndexChanged(int index) {
|
||||
if(index == -1 || index == prevIndex_ || blockOnCurrentIndexChanged_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the last item is "Customize"
|
||||
if(index == (count() - 1)) {
|
||||
/* TODO: let the user choose an app or add custom actions here. */
|
||||
QWidget* toplevel = topLevelWidget();
|
||||
AppChooserDialog dlg(mimeType_, toplevel);
|
||||
dlg.setWindowModality(Qt::WindowModal);
|
||||
dlg.setCanSetDefault(false);
|
||||
if(dlg.exec() == QDialog::Accepted) {
|
||||
auto app = dlg.selectedApp();
|
||||
if(app) {
|
||||
/* see if it's already in the list to prevent duplication */
|
||||
auto found = std::find_if(appInfos_.cbegin(), appInfos_.cend(), [&](const Fm::GAppInfoPtr& item) {
|
||||
return g_app_info_equal(app.get(), item.get());
|
||||
});
|
||||
|
||||
// inserting new items or change current index will recursively trigger onCurrentIndexChanged.
|
||||
// we need to block our handler to prevent recursive calls.
|
||||
blockOnCurrentIndexChanged_ = true;
|
||||
/* if it's already in the list, select it */
|
||||
if(found != appInfos_.cend()) {
|
||||
auto pos = found - appInfos_.cbegin();
|
||||
setCurrentIndex(pos);
|
||||
}
|
||||
else { /* if it's not found, add it to the list */
|
||||
auto it = appInfos_.insert(appInfos_.cbegin(), std::move(app));
|
||||
GIcon* gicon = g_app_info_get_icon(it->get());
|
||||
insertItem(0, Fm::IconInfo::fromGIcon(gicon)->qicon(), g_app_info_get_name(it->get()));
|
||||
setCurrentIndex(0);
|
||||
}
|
||||
blockOnCurrentIndexChanged_ = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// block our handler to prevent recursive calls.
|
||||
blockOnCurrentIndexChanged_ = true;
|
||||
// restore to previously selected item
|
||||
setCurrentIndex(prevIndex_);
|
||||
blockOnCurrentIndexChanged_ = false;
|
||||
}
|
||||
else {
|
||||
prevIndex_ = index;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
/* get a list of custom apps added with app-chooser.
|
||||
* the returned GList is owned by the combo box and shouldn't be freed. */
|
||||
const GList* AppChooserComboBox::customApps() {
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Fm
|
65
src/appchoosercombobox.h
Normal file
65
src/appchoosercombobox.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2014 - 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_APPCHOOSERCOMBOBOX_H
|
||||
#define FM_APPCHOOSERCOMBOBOX_H
|
||||
|
||||
#include "libfmqtglobals.h"
|
||||
#include <QComboBox>
|
||||
#include <libfm/fm.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "core/mimetype.h"
|
||||
#include "core/gioptrs.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API AppChooserComboBox : public QComboBox {
|
||||
Q_OBJECT
|
||||
public:
|
||||
~AppChooserComboBox();
|
||||
explicit AppChooserComboBox(QWidget* parent);
|
||||
|
||||
void setMimeType(std::shared_ptr<const Fm::MimeType> mimeType);
|
||||
|
||||
const std::shared_ptr<const Fm::MimeType>& mimeType() const {
|
||||
return mimeType_;
|
||||
}
|
||||
|
||||
Fm::GAppInfoPtr selectedApp() const;
|
||||
// const GList* customApps();
|
||||
|
||||
bool isChanged() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void onCurrentIndexChanged(int index);
|
||||
|
||||
private:
|
||||
std::shared_ptr<const Fm::MimeType> mimeType_;
|
||||
std::vector<Fm::GAppInfoPtr> appInfos_; // applications used to open the file type
|
||||
Fm::GAppInfoPtr defaultApp_; // default application used to open the file type
|
||||
int defaultAppIndex_;
|
||||
int prevIndex_;
|
||||
bool blockOnCurrentIndexChanged_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FM_APPCHOOSERCOMBOBOX_H
|
286
src/appchooserdialog.cpp
Normal file
286
src/appchooserdialog.cpp
Normal file
@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright 2010-2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
||||
* Copyright 2012-2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
|
||||
*
|
||||
* 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 "appchooserdialog.h"
|
||||
#include "ui_app-chooser-dialog.h"
|
||||
#include <QPushButton>
|
||||
#include <gio/gdesktopappinfo.h>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
AppChooserDialog::AppChooserDialog(std::shared_ptr<const Fm::MimeType> mimeType, QWidget* parent, Qt::WindowFlags f):
|
||||
QDialog(parent, f),
|
||||
ui(new Ui::AppChooserDialog()),
|
||||
mimeType_{std::move(mimeType)},
|
||||
canSetDefault_(true) {
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->appMenuView, &AppMenuView::selectionChanged, this, &AppChooserDialog::onSelectionChanged);
|
||||
connect(ui->tabWidget, &QTabWidget::currentChanged, this, &AppChooserDialog::onTabChanged);
|
||||
|
||||
if(!ui->appMenuView->isAppSelected()) {
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); // disable OK button
|
||||
}
|
||||
}
|
||||
|
||||
AppChooserDialog::~AppChooserDialog() {
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool AppChooserDialog::isSetDefault() const {
|
||||
return ui->setDefault->isChecked();
|
||||
}
|
||||
|
||||
static void on_temp_appinfo_destroy(gpointer data, GObject* /*objptr*/) {
|
||||
char* filename = (char*)data;
|
||||
if(g_unlink(filename) < 0) {
|
||||
g_critical("failed to remove %s", filename);
|
||||
}
|
||||
/* else
|
||||
qDebug("temp file %s removed", filename); */
|
||||
g_free(filename);
|
||||
}
|
||||
|
||||
static GAppInfo* app_info_create_from_commandline(const char* commandline,
|
||||
const char* application_name,
|
||||
const char* bin_name,
|
||||
const char* mime_type,
|
||||
gboolean terminal, gboolean keep) {
|
||||
GAppInfo* app = nullptr;
|
||||
char* dirname = g_build_filename(g_get_user_data_dir(), "applications", nullptr);
|
||||
const char* app_basename = strrchr(bin_name, '/');
|
||||
|
||||
if(app_basename) {
|
||||
app_basename++;
|
||||
}
|
||||
else {
|
||||
app_basename = bin_name;
|
||||
}
|
||||
if(g_mkdir_with_parents(dirname, 0700) == 0) {
|
||||
char* filename = g_strdup_printf("%s/userapp-%s-XXXXXX.desktop", dirname, app_basename);
|
||||
int fd = g_mkstemp(filename);
|
||||
if(fd != -1) {
|
||||
GString* content = g_string_sized_new(256);
|
||||
g_string_printf(content,
|
||||
"[" G_KEY_FILE_DESKTOP_GROUP "]\n"
|
||||
G_KEY_FILE_DESKTOP_KEY_TYPE "=" G_KEY_FILE_DESKTOP_TYPE_APPLICATION "\n"
|
||||
G_KEY_FILE_DESKTOP_KEY_NAME "=%s\n"
|
||||
G_KEY_FILE_DESKTOP_KEY_EXEC "=%s\n"
|
||||
G_KEY_FILE_DESKTOP_KEY_CATEGORIES "=Other;\n"
|
||||
G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY "=true\n",
|
||||
application_name,
|
||||
commandline
|
||||
);
|
||||
if(mime_type)
|
||||
g_string_append_printf(content,
|
||||
G_KEY_FILE_DESKTOP_KEY_MIME_TYPE "=%s\n",
|
||||
mime_type);
|
||||
g_string_append_printf(content,
|
||||
G_KEY_FILE_DESKTOP_KEY_TERMINAL "=%s\n",
|
||||
terminal ? "true" : "false");
|
||||
if(terminal)
|
||||
g_string_append_printf(content, "X-KeepTerminal=%s\n",
|
||||
keep ? "true" : "false");
|
||||
close(fd); /* g_file_set_contents() may fail creating duplicate */
|
||||
if(g_file_set_contents(filename, content->str, content->len, nullptr)) {
|
||||
char* fbname = g_path_get_basename(filename);
|
||||
app = G_APP_INFO(g_desktop_app_info_new(fbname));
|
||||
g_free(fbname);
|
||||
/* if there is mime_type set then created application will be
|
||||
saved for the mime type (see fm_choose_app_for_mime_type()
|
||||
below) but if not then we should remove this temp. file */
|
||||
if(!mime_type || !application_name[0])
|
||||
/* save the name so this file will be removed later */
|
||||
g_object_weak_ref(G_OBJECT(app), on_temp_appinfo_destroy,
|
||||
g_strdup(filename));
|
||||
}
|
||||
else {
|
||||
g_unlink(filename);
|
||||
}
|
||||
g_string_free(content, TRUE);
|
||||
}
|
||||
g_free(filename);
|
||||
}
|
||||
g_free(dirname);
|
||||
return app;
|
||||
}
|
||||
|
||||
inline static char* get_binary(const char* cmdline, gboolean* arg_found) {
|
||||
/* see if command line contains %f, %F, %u, or %U. */
|
||||
const char* p = strstr(cmdline, " %");
|
||||
if(p) {
|
||||
if(!strchr("fFuU", *(p + 2))) {
|
||||
p = nullptr;
|
||||
}
|
||||
}
|
||||
if(arg_found) {
|
||||
*arg_found = (p != nullptr);
|
||||
}
|
||||
if(p) {
|
||||
return g_strndup(cmdline, p - cmdline);
|
||||
}
|
||||
else {
|
||||
return g_strdup(cmdline);
|
||||
}
|
||||
}
|
||||
|
||||
GAppInfo* AppChooserDialog::customCommandToApp() {
|
||||
GAppInfo* app = nullptr;
|
||||
QByteArray cmdline = ui->cmdLine->text().toLocal8Bit();
|
||||
QByteArray app_name = ui->appName->text().toUtf8();
|
||||
if(!cmdline.isEmpty()) {
|
||||
gboolean arg_found = FALSE;
|
||||
char* bin1 = get_binary(cmdline.constData(), &arg_found);
|
||||
qDebug("bin1 = %s", bin1);
|
||||
/* see if command line contains %f, %F, %u, or %U. */
|
||||
if(!arg_found) { /* append %f if no %f, %F, %u, or %U was found. */
|
||||
cmdline += " %f";
|
||||
}
|
||||
|
||||
/* FIXME: is there any better way to do this? */
|
||||
/* We need to ensure that no duplicated items are added */
|
||||
if(mimeType_) {
|
||||
MenuCache* menu_cache;
|
||||
/* see if the command is already in the list of known apps for this mime-type */
|
||||
GList* apps = g_app_info_get_all_for_type(mimeType_->name());
|
||||
GList* l;
|
||||
for(l = apps; l; l = l->next) {
|
||||
GAppInfo* app2 = G_APP_INFO(l->data);
|
||||
const char* cmd = g_app_info_get_commandline(app2);
|
||||
char* bin2 = get_binary(cmd, nullptr);
|
||||
if(g_strcmp0(bin1, bin2) == 0) {
|
||||
app = G_APP_INFO(g_object_ref(app2));
|
||||
qDebug("found in app list");
|
||||
g_free(bin2);
|
||||
break;
|
||||
}
|
||||
g_free(bin2);
|
||||
}
|
||||
g_list_foreach(apps, (GFunc)g_object_unref, nullptr);
|
||||
g_list_free(apps);
|
||||
if(app) {
|
||||
goto _out;
|
||||
}
|
||||
|
||||
/* see if this command can be found in menu cache */
|
||||
menu_cache = menu_cache_lookup("applications.menu");
|
||||
if(menu_cache) {
|
||||
MenuCacheDir* root_dir = menu_cache_dup_root_dir(menu_cache);
|
||||
if(root_dir) {
|
||||
GSList* all_apps = menu_cache_list_all_apps(menu_cache);
|
||||
GSList* l;
|
||||
for(l = all_apps; l; l = l->next) {
|
||||
MenuCacheApp* ma = MENU_CACHE_APP(l->data);
|
||||
const char* exec = menu_cache_app_get_exec(ma);
|
||||
char* bin2;
|
||||
if(exec == nullptr) {
|
||||
g_warning("application %s has no Exec statement", menu_cache_item_get_id(MENU_CACHE_ITEM(ma)));
|
||||
continue;
|
||||
}
|
||||
bin2 = get_binary(exec, nullptr);
|
||||
if(g_strcmp0(bin1, bin2) == 0) {
|
||||
app = G_APP_INFO(g_desktop_app_info_new(menu_cache_item_get_id(MENU_CACHE_ITEM(ma))));
|
||||
qDebug("found in menu cache");
|
||||
menu_cache_item_unref(MENU_CACHE_ITEM(ma));
|
||||
g_free(bin2);
|
||||
break;
|
||||
}
|
||||
menu_cache_item_unref(MENU_CACHE_ITEM(ma));
|
||||
g_free(bin2);
|
||||
}
|
||||
g_slist_free(all_apps);
|
||||
menu_cache_item_unref(MENU_CACHE_ITEM(root_dir));
|
||||
}
|
||||
menu_cache_unref(menu_cache);
|
||||
}
|
||||
if(app) {
|
||||
goto _out;
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: g_app_info_create_from_commandline force the use of %f or %u, so this is not we need */
|
||||
app = app_info_create_from_commandline(cmdline.constData(), app_name.constData(), bin1,
|
||||
mimeType_ ? mimeType_->name() : nullptr,
|
||||
ui->useTerminal->isChecked(), ui->keepTermOpen->isChecked());
|
||||
_out:
|
||||
g_free(bin1);
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
void AppChooserDialog::accept() {
|
||||
QDialog::accept();
|
||||
|
||||
if(ui->tabWidget->currentIndex() == 0) {
|
||||
selectedApp_ = ui->appMenuView->selectedApp();
|
||||
}
|
||||
else { // custom command line
|
||||
selectedApp_ = customCommandToApp();
|
||||
}
|
||||
|
||||
if(selectedApp_) {
|
||||
if(mimeType_ && g_app_info_get_name(selectedApp_.get())) {
|
||||
/* add this app to the mime-type */
|
||||
#if GLIB_CHECK_VERSION(2, 27, 6)
|
||||
g_app_info_set_as_last_used_for_type(selectedApp_.get(), mimeType_->name(), nullptr);
|
||||
#else
|
||||
g_app_info_add_supports_type(selectedApp_.get(), mimeType_->name(), nullptr);
|
||||
#endif
|
||||
/* if need to set default */
|
||||
if(ui->setDefault->isChecked()) {
|
||||
g_app_info_set_as_default_for_type(selectedApp_.get(), mimeType_->name(), nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AppChooserDialog::onSelectionChanged() {
|
||||
bool isAppSelected = ui->appMenuView->isAppSelected();
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isAppSelected);
|
||||
}
|
||||
|
||||
void AppChooserDialog::setMimeType(std::shared_ptr<const Fm::MimeType> mimeType) {
|
||||
mimeType_ = std::move(mimeType);
|
||||
if(mimeType_) {
|
||||
QString text = tr("Select an application to open \"%1\" files")
|
||||
.arg(QString::fromUtf8(mimeType_->desc()));
|
||||
ui->fileTypeHeader->setText(text);
|
||||
}
|
||||
else {
|
||||
ui->fileTypeHeader->hide();
|
||||
ui->setDefault->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void AppChooserDialog::setCanSetDefault(bool value) {
|
||||
canSetDefault_ = value;
|
||||
ui->setDefault->setVisible(value);
|
||||
}
|
||||
|
||||
void AppChooserDialog::onTabChanged(int index) {
|
||||
if(index == 0) { // app menu view
|
||||
onSelectionChanged();
|
||||
}
|
||||
else if(index == 1) { // custom command
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Fm
|
79
src/appchooserdialog.h
Normal file
79
src/appchooserdialog.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2010-2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
||||
* Copyright 2012-2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
|
||||
*
|
||||
* 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_APPCHOOSERDIALOG_H
|
||||
#define FM_APPCHOOSERDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include "libfmqtglobals.h"
|
||||
#include <libfm/fm.h>
|
||||
|
||||
#include "core/mimetype.h"
|
||||
#include "core/gioptrs.h"
|
||||
|
||||
namespace Ui {
|
||||
class AppChooserDialog;
|
||||
}
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API AppChooserDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AppChooserDialog(std::shared_ptr<const Fm::MimeType> mimeType, QWidget* parent = nullptr, Qt::WindowFlags f = 0);
|
||||
~AppChooserDialog();
|
||||
|
||||
virtual void accept();
|
||||
|
||||
void setMimeType(std::shared_ptr<const Fm::MimeType> mimeType);
|
||||
|
||||
const std::shared_ptr<const Fm::MimeType>& mimeType() const {
|
||||
return mimeType_;
|
||||
}
|
||||
|
||||
void setCanSetDefault(bool value);
|
||||
|
||||
bool canSetDefault() const {
|
||||
return canSetDefault_;
|
||||
}
|
||||
|
||||
const Fm::GAppInfoPtr& selectedApp() const {
|
||||
return selectedApp_;
|
||||
}
|
||||
|
||||
bool isSetDefault() const;
|
||||
|
||||
private:
|
||||
GAppInfo* customCommandToApp();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onSelectionChanged();
|
||||
void onTabChanged(int index);
|
||||
|
||||
private:
|
||||
Ui::AppChooserDialog* ui;
|
||||
std::shared_ptr<const Fm::MimeType> mimeType_;
|
||||
bool canSetDefault_;
|
||||
Fm::GAppInfoPtr selectedApp_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FM_APPCHOOSERDIALOG_H
|
61
src/applaunchcontext.cpp
Normal file
61
src/applaunchcontext.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 "applaunchcontext.h"
|
||||
#include <QX11Info>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
typedef struct _FmAppLaunchContext {
|
||||
GAppLaunchContext parent;
|
||||
}FmAppLaunchContext;
|
||||
|
||||
G_DEFINE_TYPE(FmAppLaunchContext, fm_app_launch_context, G_TYPE_APP_LAUNCH_CONTEXT)
|
||||
|
||||
static char* fm_app_launch_context_get_display(GAppLaunchContext * /*context*/, GAppInfo * /*info*/, GList * /*files*/) {
|
||||
Display* dpy = QX11Info::display();
|
||||
if(dpy) {
|
||||
char* xstr = DisplayString(dpy);
|
||||
return g_strdup(xstr);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static char* fm_app_launch_context_get_startup_notify_id(GAppLaunchContext * /*context*/, GAppInfo * /*info*/, GList * /*files*/) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void fm_app_launch_context_class_init(FmAppLaunchContextClass* klass) {
|
||||
GAppLaunchContextClass* app_launch_class = G_APP_LAUNCH_CONTEXT_CLASS(klass);
|
||||
app_launch_class->get_display = fm_app_launch_context_get_display;
|
||||
app_launch_class->get_startup_notify_id = fm_app_launch_context_get_startup_notify_id;
|
||||
}
|
||||
|
||||
static void fm_app_launch_context_init(FmAppLaunchContext* /*context*/) {
|
||||
}
|
||||
|
||||
FmAppLaunchContext* fm_app_launch_context_new_for_widget(QWidget* /*widget*/) {
|
||||
FmAppLaunchContext* context = (FmAppLaunchContext*)g_object_new(FM_TYPE_APP_LAUNCH_CONTEXT, nullptr);
|
||||
return context;
|
||||
}
|
||||
|
||||
FmAppLaunchContext* fm_app_launch_context_new() {
|
||||
FmAppLaunchContext* context = (FmAppLaunchContext*)g_object_new(FM_TYPE_APP_LAUNCH_CONTEXT, nullptr);
|
||||
return context;
|
||||
}
|
50
src/applaunchcontext.h
Normal file
50
src/applaunchcontext.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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_APP_LAUNCHCONTEXT_H
|
||||
#define FM_APP_LAUNCHCONTEXT_H
|
||||
|
||||
#include "libfmqtglobals.h"
|
||||
#include <gio/gio.h>
|
||||
#include <QWidget>
|
||||
|
||||
#define FM_TYPE_APP_LAUNCH_CONTEXT (fm_app_launch_context_get_type())
|
||||
#define FM_APP_LAUNCH_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
|
||||
FM_TYPE_APP_LAUNCH_CONTEXT, FmAppLaunchContext))
|
||||
#define FM_APP_LAUNCH_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\
|
||||
FM_TYPE_APP_LAUNCH_CONTEXT, FmAppLaunchContextClass))
|
||||
#define FM_IS_APP_LAUNCH_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\
|
||||
FM_TYPE_APP_LAUNCH_CONTEXT))
|
||||
#define FM_IS_APP_LAUNCH_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\
|
||||
FM_TYPE_APP_LAUNCH_CONTEXT))
|
||||
#define FM_APP_LAUNCH_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),\
|
||||
FM_TYPE_APP_LAUNCH_CONTEXT, FmAppLaunchContextClass))
|
||||
|
||||
typedef struct _FmAppLaunchContext FmAppLaunchContext;
|
||||
|
||||
typedef struct _FmAppLaunchContextClass {
|
||||
GAppLaunchContextClass parent;
|
||||
}FmAppLaunchContextClass;
|
||||
|
||||
FmAppLaunchContext* fm_app_launch_context_new();
|
||||
FmAppLaunchContext* fm_app_launch_context_new_for_widget(QWidget* widget);
|
||||
GType fm_app_launch_context_get_type();
|
||||
|
||||
#endif // FM_APPLAUNCHCONTEXT_H
|
162
src/appmenuview.cpp
Normal file
162
src/appmenuview.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2014 - 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 "appmenuview.h"
|
||||
#include <QStandardItemModel>
|
||||
#include "icontheme.h"
|
||||
#include "appmenuview_p.h"
|
||||
#include <gio/gdesktopappinfo.h>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
AppMenuView::AppMenuView(QWidget* parent):
|
||||
QTreeView(parent),
|
||||
model_(new QStandardItemModel()),
|
||||
menu_cache(nullptr),
|
||||
menu_cache_reload_notify(nullptr) {
|
||||
|
||||
setHeaderHidden(true);
|
||||
setSelectionMode(SingleSelection);
|
||||
|
||||
// initialize model
|
||||
// TODO: share one model among all app menu view widgets
|
||||
// ensure that we're using lxmenu-data (FIXME: should we do this?)
|
||||
QByteArray oldenv = qgetenv("XDG_MENU_PREFIX");
|
||||
qputenv("XDG_MENU_PREFIX", "lxde-");
|
||||
menu_cache = menu_cache_lookup("applications.menu");
|
||||
// if(!oldenv.isEmpty())
|
||||
qputenv("XDG_MENU_PREFIX", oldenv); // restore the original value if needed
|
||||
|
||||
if(menu_cache) {
|
||||
MenuCacheDir* dir = menu_cache_dup_root_dir(menu_cache);
|
||||
menu_cache_reload_notify = menu_cache_add_reload_notify(menu_cache, _onMenuCacheReload, this);
|
||||
if(dir) { /* content of menu is already loaded */
|
||||
addMenuItems(nullptr, dir);
|
||||
menu_cache_item_unref(MENU_CACHE_ITEM(dir));
|
||||
}
|
||||
}
|
||||
setModel(model_);
|
||||
connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &AppMenuView::selectionChanged);
|
||||
selectionModel()->select(model_->index(0, 0), QItemSelectionModel::SelectCurrent);
|
||||
}
|
||||
|
||||
AppMenuView::~AppMenuView() {
|
||||
delete model_;
|
||||
if(menu_cache) {
|
||||
if(menu_cache_reload_notify) {
|
||||
menu_cache_remove_reload_notify(menu_cache, menu_cache_reload_notify);
|
||||
}
|
||||
menu_cache_unref(menu_cache);
|
||||
}
|
||||
}
|
||||
|
||||
void AppMenuView::addMenuItems(QStandardItem* parentItem, MenuCacheDir* dir) {
|
||||
GSList* l;
|
||||
GSList* list;
|
||||
/* Iterate over all menu items in this directory. */
|
||||
for(l = list = menu_cache_dir_list_children(dir); l != nullptr; l = l->next) {
|
||||
/* Get the menu item. */
|
||||
MenuCacheItem* menuItem = MENU_CACHE_ITEM(l->data);
|
||||
switch(menu_cache_item_get_type(menuItem)) {
|
||||
case MENU_CACHE_TYPE_NONE:
|
||||
case MENU_CACHE_TYPE_SEP:
|
||||
break;
|
||||
case MENU_CACHE_TYPE_APP:
|
||||
case MENU_CACHE_TYPE_DIR: {
|
||||
AppMenuViewItem* newItem = new AppMenuViewItem(menuItem);
|
||||
if(parentItem) {
|
||||
parentItem->insertRow(parentItem->rowCount(), newItem);
|
||||
}
|
||||
else {
|
||||
model_->insertRow(model_->rowCount(), newItem);
|
||||
}
|
||||
|
||||
if(menu_cache_item_get_type(menuItem) == MENU_CACHE_TYPE_DIR) {
|
||||
addMenuItems(newItem, MENU_CACHE_DIR(menuItem));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_slist_free_full(list, (GDestroyNotify)menu_cache_item_unref);
|
||||
}
|
||||
|
||||
void AppMenuView::onMenuCacheReload(MenuCache* mc) {
|
||||
MenuCacheDir* dir = menu_cache_dup_root_dir(mc);
|
||||
model_->clear();
|
||||
/* FIXME: preserve original selection */
|
||||
if(dir) {
|
||||
addMenuItems(nullptr, dir);
|
||||
menu_cache_item_unref(MENU_CACHE_ITEM(dir));
|
||||
selectionModel()->select(model_->index(0, 0), QItemSelectionModel::SelectCurrent);
|
||||
}
|
||||
}
|
||||
|
||||
bool AppMenuView::isAppSelected() const {
|
||||
AppMenuViewItem* item = selectedItem();
|
||||
return (item && item->isApp());
|
||||
}
|
||||
|
||||
AppMenuViewItem* AppMenuView::selectedItem() const {
|
||||
QModelIndexList selected = selectedIndexes();
|
||||
if(!selected.isEmpty()) {
|
||||
AppMenuViewItem* item = static_cast<AppMenuViewItem*>(model_->itemFromIndex(selected.first()
|
||||
));
|
||||
return item;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Fm::GAppInfoPtr AppMenuView::selectedApp() const {
|
||||
const char* id = selectedAppDesktopId();
|
||||
return Fm::GAppInfoPtr{id ? G_APP_INFO(g_desktop_app_info_new(id)) : nullptr, false};
|
||||
}
|
||||
|
||||
QByteArray AppMenuView::selectedAppDesktopFilePath() const {
|
||||
AppMenuViewItem* item = selectedItem();
|
||||
if(item && item->isApp()) {
|
||||
char* path = menu_cache_item_get_file_path(item->item());
|
||||
QByteArray ret(path);
|
||||
g_free(path);
|
||||
return ret;
|
||||
}
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
const char* AppMenuView::selectedAppDesktopId() const {
|
||||
AppMenuViewItem* item = selectedItem();
|
||||
if(item && item->isApp()) {
|
||||
return menu_cache_item_get_id(item->item());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FmPath* AppMenuView::selectedAppDesktopPath() const {
|
||||
AppMenuViewItem* item = selectedItem();
|
||||
if(item && item->isApp()) {
|
||||
char* mpath = menu_cache_dir_make_path(MENU_CACHE_DIR(item));
|
||||
FmPath* path = fm_path_new_relative(fm_path_get_apps_menu(),
|
||||
mpath + 13 /* skip "/Applications" */);
|
||||
g_free(mpath);
|
||||
return path;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Fm
|
74
src/appmenuview.h
Normal file
74
src/appmenuview.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2014 - 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_APPMENUVIEW_H
|
||||
#define FM_APPMENUVIEW_H
|
||||
|
||||
#include <QTreeView>
|
||||
#include "libfmqtglobals.h"
|
||||
#include <libfm/fm.h>
|
||||
#include <menu-cache/menu-cache.h>
|
||||
|
||||
#include "core/gioptrs.h"
|
||||
|
||||
class QStandardItemModel;
|
||||
class QStandardItem;
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class AppMenuViewItem;
|
||||
|
||||
class LIBFM_QT_API AppMenuView : public QTreeView {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AppMenuView(QWidget* parent = nullptr);
|
||||
~AppMenuView();
|
||||
|
||||
Fm::GAppInfoPtr selectedApp() const;
|
||||
|
||||
const char* selectedAppDesktopId() const;
|
||||
|
||||
QByteArray selectedAppDesktopFilePath() const;
|
||||
|
||||
FmPath* selectedAppDesktopPath() const;
|
||||
|
||||
bool isAppSelected() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
|
||||
|
||||
private:
|
||||
void addMenuItems(QStandardItem* parentItem, MenuCacheDir* dir);
|
||||
void onMenuCacheReload(MenuCache* mc);
|
||||
static void _onMenuCacheReload(MenuCache* mc, gpointer user_data) {
|
||||
static_cast<AppMenuView*>(user_data)->onMenuCacheReload(mc);
|
||||
}
|
||||
|
||||
AppMenuViewItem* selectedItem() const;
|
||||
|
||||
private:
|
||||
// gboolean fm_app_menu_view_is_item_app(, GtkTreeIter* it);
|
||||
QStandardItemModel* model_;
|
||||
MenuCache* menu_cache;
|
||||
MenuCacheNotifyId menu_cache_reload_notify;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FM_APPMENUVIEW_H
|
72
src/appmenuview_p.h
Normal file
72
src/appmenuview_p.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2014 - 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_APPMENUVIEW_P_H
|
||||
#define FM_APPMENUVIEW_P_H
|
||||
|
||||
#include <QStandardItem>
|
||||
#include <menu-cache/menu-cache.h>
|
||||
#include "icontheme.h"
|
||||
#include "core/iconinfo.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class AppMenuViewItem : public QStandardItem {
|
||||
public:
|
||||
explicit AppMenuViewItem(MenuCacheItem* item):
|
||||
item_(menu_cache_item_ref(item)) {
|
||||
std::shared_ptr<const Fm::IconInfo> icon;
|
||||
if(menu_cache_item_get_icon(item)) {
|
||||
icon = Fm::IconInfo::fromName(menu_cache_item_get_icon(item));
|
||||
}
|
||||
setText(menu_cache_item_get_name(item));
|
||||
setEditable(false);
|
||||
setDragEnabled(false);
|
||||
if(icon) {
|
||||
setIcon(icon->qicon());
|
||||
}
|
||||
}
|
||||
|
||||
~AppMenuViewItem() {
|
||||
menu_cache_item_unref(item_);
|
||||
}
|
||||
|
||||
MenuCacheItem* item() {
|
||||
return item_;
|
||||
}
|
||||
|
||||
int type() const {
|
||||
return menu_cache_item_get_type(item_);
|
||||
}
|
||||
|
||||
bool isApp() {
|
||||
return type() == MENU_CACHE_TYPE_APP;
|
||||
}
|
||||
|
||||
bool isDir() {
|
||||
return type() == MENU_CACHE_TYPE_DIR;
|
||||
}
|
||||
|
||||
private:
|
||||
MenuCacheItem* item_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FM_APPMENUVIEW_P_H
|
143
src/archiver.h
Normal file
143
src/archiver.h
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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__
|
32
src/bookmarkaction.cpp
Normal file
32
src/bookmarkaction.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2013 - 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 "bookmarkaction.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
BookmarkAction::BookmarkAction(std::shared_ptr<const Fm::BookmarkItem> item, QObject* parent):
|
||||
QAction(parent),
|
||||
item_(std::move(item)) {
|
||||
|
||||
setText(item_->name());
|
||||
}
|
||||
|
||||
} // namespace Fm
|
49
src/bookmarkaction.h
Normal file
49
src/bookmarkaction.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2013 - 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 BOOKMARKACTION_H
|
||||
#define BOOKMARKACTION_H
|
||||
|
||||
#include "libfmqtglobals.h"
|
||||
#include <QAction>
|
||||
#include "core/bookmarks.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
// action used to create bookmark menu items
|
||||
class LIBFM_QT_API BookmarkAction : public QAction {
|
||||
public:
|
||||
explicit BookmarkAction(std::shared_ptr<const Fm::BookmarkItem> item, QObject* parent = 0);
|
||||
|
||||
const std::shared_ptr<const Fm::BookmarkItem>& bookmark() const {
|
||||
return item_;
|
||||
}
|
||||
|
||||
const Fm::FilePath& path() const {
|
||||
return item_->path();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<const Fm::BookmarkItem> item_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // BOOKMARKACTION_H
|
93
src/browsehistory.cpp
Normal file
93
src/browsehistory.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2013 - 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 "browsehistory.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
BrowseHistory::BrowseHistory():
|
||||
currentIndex_(0),
|
||||
maxCount_(10) {
|
||||
}
|
||||
|
||||
BrowseHistory::~BrowseHistory() {
|
||||
}
|
||||
|
||||
void BrowseHistory::add(Fm::FilePath path, int scrollPos) {
|
||||
int lastIndex = items_.size() - 1;
|
||||
if(currentIndex_ < lastIndex) {
|
||||
// if we're not at the last item, remove items after the current one.
|
||||
items_.erase(items_.cbegin() + currentIndex_ + 1, items_.cend());
|
||||
}
|
||||
|
||||
if(items_.size() + 1 > static_cast<size_t>(maxCount_)) {
|
||||
// if there are too many items, remove the oldest one.
|
||||
// FIXME: what if currentIndex_ == 0? remove the last item instead?
|
||||
if(currentIndex_ == 0) {
|
||||
items_.erase(items_.cbegin() + lastIndex);
|
||||
}
|
||||
else {
|
||||
items_.erase(items_.cbegin());
|
||||
--currentIndex_;
|
||||
}
|
||||
}
|
||||
// add a path and current scroll position to browse history
|
||||
items_.push_back(BrowseHistoryItem(path, scrollPos));
|
||||
currentIndex_ = items_.size() - 1;
|
||||
}
|
||||
|
||||
void BrowseHistory::setCurrentIndex(int index) {
|
||||
if(index >= 0 && static_cast<size_t>(index) < items_.size()) {
|
||||
currentIndex_ = index;
|
||||
// FIXME: should we emit a signal for the change?
|
||||
}
|
||||
}
|
||||
|
||||
bool BrowseHistory::canBackward() const {
|
||||
return (currentIndex_ > 0);
|
||||
}
|
||||
|
||||
int BrowseHistory::backward() {
|
||||
if(canBackward()) {
|
||||
--currentIndex_;
|
||||
}
|
||||
return currentIndex_;
|
||||
}
|
||||
|
||||
bool BrowseHistory::canForward() const {
|
||||
return (static_cast<size_t>(currentIndex_) + 1 < items_.size());
|
||||
}
|
||||
|
||||
int BrowseHistory::forward() {
|
||||
if(canForward()) {
|
||||
++currentIndex_;
|
||||
}
|
||||
return currentIndex_;
|
||||
}
|
||||
|
||||
void BrowseHistory::setMaxCount(int maxCount) {
|
||||
maxCount_ = maxCount;
|
||||
if(items_.size() > static_cast<size_t>(maxCount)) {
|
||||
// TODO: remove some items
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace Fm
|
132
src/browsehistory.h
Normal file
132
src/browsehistory.h
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2013 - 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_BROWSEHISTORY_H
|
||||
#define FM_BROWSEHISTORY_H
|
||||
|
||||
#include "libfmqtglobals.h"
|
||||
#include <vector>
|
||||
#include <libfm/fm.h>
|
||||
|
||||
#include "core/filepath.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
// class used to story browsing history of folder views
|
||||
// We use this class to replace FmNavHistory provided by libfm since
|
||||
// the original Libfm API is hard to use and confusing.
|
||||
|
||||
class LIBFM_QT_API BrowseHistoryItem {
|
||||
public:
|
||||
|
||||
explicit BrowseHistoryItem():
|
||||
scrollPos_(0) {
|
||||
}
|
||||
|
||||
explicit BrowseHistoryItem(Fm::FilePath path, int scrollPos = 0):
|
||||
path_(std::move(path)),
|
||||
scrollPos_(scrollPos) {
|
||||
}
|
||||
|
||||
BrowseHistoryItem(const BrowseHistoryItem& other) = default;
|
||||
|
||||
~BrowseHistoryItem() {
|
||||
}
|
||||
|
||||
BrowseHistoryItem& operator=(const BrowseHistoryItem& other) {
|
||||
path_ = other.path_;
|
||||
scrollPos_ = other.scrollPos_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fm::FilePath path() const {
|
||||
return path_;
|
||||
}
|
||||
|
||||
int scrollPos() const {
|
||||
return scrollPos_;
|
||||
}
|
||||
|
||||
void setScrollPos(int pos) {
|
||||
scrollPos_ = pos;
|
||||
}
|
||||
|
||||
private:
|
||||
Fm::FilePath path_;
|
||||
int scrollPos_;
|
||||
// TODO: we may need to store current selection as well.
|
||||
};
|
||||
|
||||
class LIBFM_QT_API BrowseHistory {
|
||||
|
||||
public:
|
||||
BrowseHistory();
|
||||
virtual ~BrowseHistory();
|
||||
|
||||
int currentIndex() const {
|
||||
return currentIndex_;
|
||||
}
|
||||
void setCurrentIndex(int index);
|
||||
|
||||
Fm::FilePath currentPath() const {
|
||||
return items_[currentIndex_].path();
|
||||
}
|
||||
|
||||
int currentScrollPos() const {
|
||||
return items_[currentIndex_].scrollPos();
|
||||
}
|
||||
|
||||
BrowseHistoryItem& currentItem() {
|
||||
return items_[currentIndex_];
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return items_.size();
|
||||
}
|
||||
|
||||
BrowseHistoryItem& at(int index) {
|
||||
return items_[index];
|
||||
}
|
||||
|
||||
void add(Fm::FilePath path, int scrollPos = 0);
|
||||
|
||||
bool canForward() const;
|
||||
|
||||
bool canBackward() const;
|
||||
|
||||
int backward();
|
||||
|
||||
int forward();
|
||||
|
||||
int maxCount() const {
|
||||
return maxCount_;
|
||||
}
|
||||
|
||||
void setMaxCount(int maxCount);
|
||||
|
||||
private:
|
||||
std::vector<BrowseHistoryItem> items_;
|
||||
int currentIndex_;
|
||||
int maxCount_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FM_BROWSEHISTORY_H
|
67
src/cachedfoldermodel.cpp
Normal file
67
src/cachedfoldermodel.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2013 - 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 "cachedfoldermodel.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
CachedFolderModel::CachedFolderModel(const std::shared_ptr<Fm::Folder>& folder):
|
||||
FolderModel(),
|
||||
refCount(1) {
|
||||
FolderModel::setFolder(folder);
|
||||
}
|
||||
|
||||
CachedFolderModel::~CachedFolderModel() {
|
||||
// qDebug("delete CachedFolderModel");
|
||||
}
|
||||
|
||||
CachedFolderModel* CachedFolderModel::modelFromFolder(const std::shared_ptr<Fm::Folder>& folder) {
|
||||
QVariant cache = folder->property(cacheKey);
|
||||
CachedFolderModel* model = cache.value<CachedFolderModel*>();
|
||||
if(model) {
|
||||
model->ref();
|
||||
}
|
||||
else {
|
||||
model = new CachedFolderModel(folder);
|
||||
cache = QVariant::fromValue(model);
|
||||
folder->setProperty(cacheKey, cache);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
CachedFolderModel* CachedFolderModel::modelFromPath(const Fm::FilePath& path) {
|
||||
auto folder = Fm::Folder::fromPath(path);
|
||||
if(folder) {
|
||||
CachedFolderModel* model = modelFromFolder(folder);
|
||||
return model;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CachedFolderModel::unref() {
|
||||
// qDebug("unref cache");
|
||||
--refCount;
|
||||
if(refCount <= 0) {
|
||||
folder()->setProperty(cacheKey, QVariant());
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace Fm
|
55
src/cachedfoldermodel.h
Normal file
55
src/cachedfoldermodel.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2013 - 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_CACHEDFOLDERMODEL_H
|
||||
#define FM_CACHEDFOLDERMODEL_H
|
||||
|
||||
#include "libfmqtglobals.h"
|
||||
#include "foldermodel.h"
|
||||
|
||||
#include "core/folder.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
// FIXME: deprecate CachedFolderModel later (ugly API design with manual ref()/unref())
|
||||
class LIBFM_QT_API CachedFolderModel : public FolderModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit CachedFolderModel(const std::shared_ptr<Fm::Folder>& folder);
|
||||
void ref() {
|
||||
++refCount;
|
||||
}
|
||||
void unref();
|
||||
|
||||
static CachedFolderModel* modelFromFolder(const std::shared_ptr<Fm::Folder>& folder);
|
||||
static CachedFolderModel* modelFromPath(const Fm::FilePath& path);
|
||||
|
||||
private:
|
||||
virtual ~CachedFolderModel();
|
||||
void setFolder(FmFolder* folder);
|
||||
private:
|
||||
int refCount;
|
||||
constexpr static const char* cacheKey = "CachedFolderModel";
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // FM_CACHEDFOLDERMODEL_H
|
53
src/colorbutton.cpp
Normal file
53
src/colorbutton.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2013 - 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 "colorbutton.h"
|
||||
#include <QColorDialog>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
ColorButton::ColorButton(QWidget* parent): QPushButton(parent) {
|
||||
connect(this, &QPushButton::clicked, this, &ColorButton::onClicked);
|
||||
}
|
||||
|
||||
ColorButton::~ColorButton() {
|
||||
|
||||
}
|
||||
|
||||
void ColorButton::onClicked() {
|
||||
QColorDialog dlg(color_);
|
||||
if(dlg.exec() == QDialog::Accepted) {
|
||||
setColor(dlg.selectedColor());
|
||||
}
|
||||
}
|
||||
|
||||
void ColorButton::setColor(const QColor& color) {
|
||||
if(color != color_) {
|
||||
color_ = color;
|
||||
// use qss instead of QPalette to set the background color
|
||||
// otherwise, this won't work when using the gtk style.
|
||||
QString style = QString("QPushButton{background-color:%1;}").arg(color.name());
|
||||
setStyleSheet(style);
|
||||
Q_EMIT changed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace Fm
|
55
src/colorbutton.h
Normal file
55
src/colorbutton.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2013 - 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_COLORBUTTON_H
|
||||
#define FM_COLORBUTTON_H
|
||||
|
||||
#include "libfmqtglobals.h"
|
||||
#include <QPushButton>
|
||||
#include <QColor>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API ColorButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ColorButton(QWidget* parent = 0);
|
||||
virtual ~ColorButton();
|
||||
|
||||
void setColor(const QColor&);
|
||||
|
||||
QColor color() const {
|
||||
return color_;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void changed();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onClicked();
|
||||
|
||||
private:
|
||||
QColor color_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FM_COLORBUTTON_H
|
162
src/core/bookmarks.cpp
Normal file
162
src/core/bookmarks.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
#include "bookmarks.h"
|
||||
#include "cstrptr.h"
|
||||
#include <algorithm>
|
||||
#include <QTimer>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
std::weak_ptr<Bookmarks> Bookmarks::globalInstance_;
|
||||
|
||||
static inline CStrPtr get_legacy_bookmarks_file(void) {
|
||||
return CStrPtr{g_build_filename(g_get_home_dir(), ".gtk-bookmarks", nullptr)};
|
||||
}
|
||||
|
||||
static inline CStrPtr get_new_bookmarks_file(void) {
|
||||
return CStrPtr{g_build_filename(g_get_user_config_dir(), "gtk-3.0", "bookmarks", nullptr)};
|
||||
}
|
||||
|
||||
Bookmarks::Bookmarks(QObject* parent):
|
||||
QObject(parent),
|
||||
idle_handler{false} {
|
||||
|
||||
/* trying the gtk-3.0 first and use it if it exists */
|
||||
auto fpath = get_new_bookmarks_file();
|
||||
file = FilePath::fromLocalPath(fpath.get());
|
||||
load();
|
||||
if(items_.empty()) { /* not found, use legacy file */
|
||||
fpath = get_legacy_bookmarks_file();
|
||||
file = FilePath::fromLocalPath(fpath.get());
|
||||
load();
|
||||
}
|
||||
mon = GObjectPtr<GFileMonitor>{g_file_monitor_file(file.gfile().get(), G_FILE_MONITOR_NONE, nullptr, nullptr), false};
|
||||
if(mon) {
|
||||
g_signal_connect(mon.get(), "changed", G_CALLBACK(_onFileChanged), this);
|
||||
}
|
||||
}
|
||||
|
||||
Bookmarks::~Bookmarks() {
|
||||
if(mon) {
|
||||
g_signal_handlers_disconnect_by_data(mon.get(), this);
|
||||
}
|
||||
}
|
||||
|
||||
const std::shared_ptr<const BookmarkItem>& Bookmarks::insert(const FilePath& path, const QString& name, int pos) {
|
||||
const auto insert_pos = (pos < 0 || static_cast<size_t>(pos) > items_.size()) ? items_.cend() : items_.cbegin() + pos;
|
||||
auto it = items_.insert(insert_pos, std::make_shared<const BookmarkItem>(path, name));
|
||||
queueSave();
|
||||
return *it;
|
||||
}
|
||||
|
||||
void Bookmarks::remove(const std::shared_ptr<const BookmarkItem>& item) {
|
||||
items_.erase(std::remove(items_.begin(), items_.end(), item), items_.end());
|
||||
queueSave();
|
||||
}
|
||||
|
||||
void Bookmarks::reorder(const std::shared_ptr<const BookmarkItem>& item, int pos) {
|
||||
auto old_it = std::find(items_.cbegin(), items_.cend(), item);
|
||||
if(old_it == items_.cend())
|
||||
return;
|
||||
std::shared_ptr<const BookmarkItem> newItem = item;
|
||||
auto old_pos = old_it - items_.cbegin();
|
||||
items_.erase(old_it);
|
||||
if(old_pos < pos)
|
||||
--pos;
|
||||
auto new_it = items_.cbegin() + pos;
|
||||
if(new_it > items_.cend())
|
||||
new_it = items_.cend();
|
||||
items_.insert(new_it, std::move(newItem));
|
||||
queueSave();
|
||||
}
|
||||
|
||||
void Bookmarks::rename(const std::shared_ptr<const BookmarkItem>& item, QString new_name) {
|
||||
auto it = std::find_if(items_.cbegin(), items_.cend(), [item](const std::shared_ptr<const BookmarkItem>& elem) {
|
||||
return elem->path() == item->path();
|
||||
});
|
||||
if(it != items_.cend()) {
|
||||
// create a new item to replace the old one
|
||||
// we do not modify the old item directly since this data structure is shared with others
|
||||
it = items_.insert(it, std::make_shared<const BookmarkItem>(item->path(), new_name));
|
||||
items_.erase(it + 1); // remove the old item
|
||||
queueSave();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Bookmarks> Bookmarks::globalInstance() {
|
||||
auto bookmarks = globalInstance_.lock();
|
||||
if(!bookmarks) {
|
||||
bookmarks = std::make_shared<Bookmarks>();
|
||||
globalInstance_ = bookmarks;
|
||||
}
|
||||
return bookmarks;
|
||||
}
|
||||
|
||||
void Bookmarks::save() {
|
||||
std::string buf;
|
||||
// G_LOCK(bookmarks);
|
||||
for(auto& item: items_) {
|
||||
auto uri = item->path().uri();
|
||||
buf += uri.get();
|
||||
buf += ' ';
|
||||
buf += item->name().toUtf8().constData();
|
||||
buf += '\n';
|
||||
}
|
||||
idle_handler = false;
|
||||
// G_UNLOCK(bookmarks);
|
||||
GError* err = nullptr;
|
||||
if(!g_file_replace_contents(file.gfile().get(), buf.c_str(), buf.length(), nullptr,
|
||||
FALSE, G_FILE_CREATE_NONE, nullptr, nullptr, &err)) {
|
||||
g_critical("%s", err->message);
|
||||
g_error_free(err);
|
||||
}
|
||||
/* we changed bookmarks list, let inform who interested in that */
|
||||
Q_EMIT changed();
|
||||
}
|
||||
|
||||
void Bookmarks::load() {
|
||||
auto fpath = file.localPath();
|
||||
FILE* f;
|
||||
char buf[1024];
|
||||
/* load the file */
|
||||
f = fopen(fpath.get(), "r");
|
||||
if(f) {
|
||||
while(fgets(buf, 1024, f)) {
|
||||
// format of each line in the bookmark file:
|
||||
// <URI> <name>\n
|
||||
char* sep;
|
||||
sep = strchr(buf, '\n');
|
||||
if(sep) {
|
||||
*sep = '\0';
|
||||
}
|
||||
|
||||
QString name;
|
||||
sep = strchr(buf, ' '); // find the separator between URI and name
|
||||
if(sep) {
|
||||
*sep = '\0';
|
||||
name = sep + 1;
|
||||
}
|
||||
auto uri = buf;
|
||||
if(uri[0] != '\0') {
|
||||
items_.push_back(std::make_shared<BookmarkItem>(FilePath::fromUri(uri), name));
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
|
||||
void Bookmarks::onFileChanged(GFileMonitor* /*mon*/, GFile* /*gf*/, GFile* /*other*/, GFileMonitorEvent /*evt*/) {
|
||||
// reload the bookmarks
|
||||
items_.clear();
|
||||
load();
|
||||
Q_EMIT changed();
|
||||
}
|
||||
|
||||
|
||||
void Bookmarks::queueSave() {
|
||||
if(!idle_handler) {
|
||||
QTimer::singleShot(0, this, &Bookmarks::save);
|
||||
idle_handler = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace Fm
|
96
src/core/bookmarks.h
Normal file
96
src/core/bookmarks.h
Normal file
@ -0,0 +1,96 @@
|
||||
#ifndef FM2_BOOKMARKS_H
|
||||
#define FM2_BOOKMARKS_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include <QObject>
|
||||
#include "gobjectptr.h"
|
||||
#include "fileinfo.h"
|
||||
|
||||
|
||||
namespace Fm {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
const QString& name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
const FilePath& path() const {
|
||||
return path_;
|
||||
}
|
||||
|
||||
const std::shared_ptr<const FmFileInfo>& info() const {
|
||||
return info_;
|
||||
}
|
||||
|
||||
private:
|
||||
void setInfo(const std::shared_ptr<const FmFileInfo>& info) {
|
||||
info_ = info;
|
||||
}
|
||||
|
||||
void setName(const QString& name) {
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
private:
|
||||
FilePath path_;
|
||||
QString name_;
|
||||
std::shared_ptr<const FmFileInfo> info_;
|
||||
};
|
||||
|
||||
|
||||
class LIBFM_QT_API Bookmarks : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Bookmarks(QObject* parent = 0);
|
||||
|
||||
~Bookmarks();
|
||||
|
||||
const std::shared_ptr<const BookmarkItem> &insert(const FilePath& path, const QString& name, int pos);
|
||||
|
||||
void remove(const std::shared_ptr<const BookmarkItem>& item);
|
||||
|
||||
void reorder(const std::shared_ptr<const BookmarkItem> &item, int pos);
|
||||
|
||||
void rename(const std::shared_ptr<const BookmarkItem>& item, QString new_name);
|
||||
|
||||
const std::vector<std::shared_ptr<const BookmarkItem>>& items() const {
|
||||
return items_;
|
||||
}
|
||||
|
||||
static std::shared_ptr<Bookmarks> globalInstance();
|
||||
|
||||
Q_SIGNALS:
|
||||
void changed();
|
||||
|
||||
private Q_SLOTS:
|
||||
void save();
|
||||
|
||||
private:
|
||||
void load();
|
||||
void queueSave();
|
||||
|
||||
static void _onFileChanged(GFileMonitor* mon, GFile* gf, GFile* other, GFileMonitorEvent evt, Bookmarks* _this) {
|
||||
_this->onFileChanged(mon, gf, other, evt);
|
||||
}
|
||||
void onFileChanged(GFileMonitor* mon, GFile* gf, GFile* other, GFileMonitorEvent evt);
|
||||
|
||||
private:
|
||||
FilePath file;
|
||||
GObjectPtr<GFileMonitor> mon;
|
||||
std::vector<std::shared_ptr<const BookmarkItem>> items_;
|
||||
static std::weak_ptr<Bookmarks> globalInstance_;
|
||||
bool idle_handler;
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_BOOKMARKS_H
|
53
src/core/compat_p.h
Normal file
53
src/core/compat_p.h
Normal file
@ -0,0 +1,53 @@
|
||||
#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
|
453
src/core/copyjob.cpp
Normal file
453
src/core/copyjob.cpp
Normal file
@ -0,0 +1,453 @@
|
||||
#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
|
46
src/core/copyjob.h
Normal file
46
src/core/copyjob.h
Normal file
@ -0,0 +1,46 @@
|
||||
#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
|
42
src/core/cstrptr.h
Normal file
42
src/core/cstrptr.h
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef FM2_CSTRPTR_H
|
||||
#define FM2_CSTRPTR_H
|
||||
|
||||
#include <memory>
|
||||
#include <glib.h>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
struct CStrDeleter {
|
||||
void operator()(char* ptr) {
|
||||
g_free(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// smart pointer for C string (char*) which should be freed by free()
|
||||
typedef std::unique_ptr<char[], CStrDeleter> CStrPtr;
|
||||
|
||||
struct CStrHash {
|
||||
std::size_t operator()(const char* str) const {
|
||||
return g_str_hash(str);
|
||||
}
|
||||
};
|
||||
|
||||
struct CStrEqual {
|
||||
bool operator()(const char* str1, const char* str2) const {
|
||||
return g_str_equal(str1, str2);
|
||||
}
|
||||
};
|
||||
|
||||
struct CStrVDeleter {
|
||||
void operator()(char** ptr) {
|
||||
g_strfreev(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// smart pointer for C string array (char**) which should be freed by g_strfreev() of glib
|
||||
typedef std::unique_ptr<char*[], CStrVDeleter> CStrArrayPtr;
|
||||
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_CSTRPTR_H
|
151
src/core/deletejob.cpp
Normal file
151
src/core/deletejob.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
#include "deletejob.h"
|
||||
#include "totalsizejob.h"
|
||||
#include "fileinfo_p.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
bool DeleteJob::deleteFile(const FilePath& path, GFileInfoPtr inf) {
|
||||
ErrorAction act = ErrorAction::CONTINUE;
|
||||
while(!inf) {
|
||||
GErrorPtr err;
|
||||
inf = GFileInfoPtr{
|
||||
g_file_query_info(path.gfile().get(), "standard::*",
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable().get(), &err),
|
||||
false
|
||||
};
|
||||
if(err) {
|
||||
act = emitError(err, ErrorSeverity::SEVERE);
|
||||
if(act == ErrorAction::ABORT) {
|
||||
return false;
|
||||
}
|
||||
if(act != ErrorAction::RETRY) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* currently processed file. */
|
||||
setCurrentFile(path);
|
||||
|
||||
if(g_file_info_get_file_type(inf.get()) == G_FILE_TYPE_DIRECTORY) {
|
||||
// delete the content of the dir prior to deleting itself
|
||||
deleteDirContent(path, inf);
|
||||
}
|
||||
|
||||
bool hasError = false;
|
||||
while(!isCancelled()) {
|
||||
GErrorPtr err;
|
||||
// try to delete the path directly
|
||||
if(g_file_delete(path.gfile().get(), cancellable().get(), &err)) {
|
||||
break;
|
||||
}
|
||||
if(err) {
|
||||
// FIXME: error handling
|
||||
/* if it's non-empty dir then descent into it then try again */
|
||||
/* trash root gives G_IO_ERROR_PERMISSION_DENIED */
|
||||
if(err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_NOT_EMPTY) {
|
||||
deleteDirContent(path, inf);
|
||||
}
|
||||
else if(err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_PERMISSION_DENIED) {
|
||||
/* special case for trash:/// */
|
||||
/* FIXME: is there any better way to handle this? */
|
||||
auto scheme = path.uriScheme();
|
||||
if(g_strcmp0(scheme.get(), "trash") == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
act = emitError(err, ErrorSeverity::MODERATE);
|
||||
if(act != ErrorAction::RETRY) {
|
||||
hasError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addFinishedAmount(g_file_info_get_size(inf.get()), 1);
|
||||
|
||||
return !hasError;
|
||||
}
|
||||
|
||||
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_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable().get(), &err),
|
||||
false
|
||||
};
|
||||
if(!enu) {
|
||||
emitError(err, ErrorSeverity::MODERATE);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasError = false;
|
||||
while(!isCancelled()) {
|
||||
inf = GFileInfoPtr{
|
||||
g_file_enumerator_next_file(enu.get(), cancellable().get(), &err),
|
||||
false
|
||||
};
|
||||
if(inf) {
|
||||
auto subPath = path.child(g_file_info_get_name(inf.get()));
|
||||
if(!deleteFile(subPath, inf)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(err) {
|
||||
emitError(err, ErrorSeverity::MODERATE);
|
||||
/* ErrorAction::RETRY is not supported here */
|
||||
hasError = true;
|
||||
}
|
||||
else { /* EOF */
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_file_enumerator_close(enu.get(), nullptr, nullptr);
|
||||
return !hasError;
|
||||
}
|
||||
|
||||
|
||||
void DeleteJob::exec() {
|
||||
/* prepare the job, count total work needed with FmDeepCountJob */
|
||||
TotalSizeJob totalSizeJob{paths_, TotalSizeJob::Flags::PREPARE_DELETE};
|
||||
connect(&totalSizeJob, &TotalSizeJob::error, this, &DeleteJob::error);
|
||||
connect(this, &DeleteJob::cancelled, &totalSizeJob, &TotalSizeJob::cancel);
|
||||
totalSizeJob.run();
|
||||
|
||||
if(isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTotalAmount(totalSizeJob.totalSize(), totalSizeJob.fileCount());
|
||||
Q_EMIT preparedToRun();
|
||||
|
||||
for(auto& path : paths_) {
|
||||
if(isCancelled()) {
|
||||
break;
|
||||
}
|
||||
deleteFile(path, GFileInfoPtr{nullptr});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Fm
|
36
src/core/deletejob.h
Normal file
36
src/core/deletejob.h
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef FM2_DELETEJOB_H
|
||||
#define FM2_DELETEJOB_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include "fileoperationjob.h"
|
||||
#include "filepath.h"
|
||||
#include "gioptrs.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API DeleteJob : public Fm::FileOperationJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DeleteJob(const FilePathList& paths): paths_{paths} {
|
||||
}
|
||||
|
||||
explicit DeleteJob(FilePathList&& paths): paths_{paths} {
|
||||
}
|
||||
|
||||
~DeleteJob() {
|
||||
}
|
||||
|
||||
protected:
|
||||
void exec() override;
|
||||
|
||||
private:
|
||||
bool deleteFile(const FilePath& path, GFileInfoPtr inf);
|
||||
bool deleteDirContent(const FilePath& path, GFileInfoPtr inf);
|
||||
|
||||
private:
|
||||
FilePathList paths_;
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_DELETEJOB_H
|
178
src/core/dirlistjob.cpp
Normal file
178
src/core/dirlistjob.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
#include "dirlistjob.h"
|
||||
#include <gio/gio.h>
|
||||
#include "fileinfo_p.h"
|
||||
#include "gioptrs.h"
|
||||
#include <QDebug>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
DirListJob::DirListJob(const FilePath& path, Flags _flags, const std::shared_ptr<const HashSet>& cutFilesHashSet):
|
||||
dir_path{path}, flags{_flags}, cutFilesHashSet_{cutFilesHashSet} {
|
||||
}
|
||||
|
||||
void DirListJob::exec() {
|
||||
GErrorPtr err;
|
||||
GFileInfoPtr dir_inf;
|
||||
GFilePtr dir_gfile = dir_path.gfile();
|
||||
// FIXME: these are hacks for search:/// URI implemented by libfm which contains some bugs
|
||||
bool isFileSearch = dir_path.hasUriScheme("search");
|
||||
if(isFileSearch) {
|
||||
// NOTE: The GFile instance changes its URI during file enumeration (bad design).
|
||||
// So we create a copy here to avoid channging the gfile stored in dir_path.
|
||||
// FIXME: later we should refactor file search and remove this dirty hack.
|
||||
dir_gfile = GFilePtr{g_file_dup(dir_gfile.get())};
|
||||
}
|
||||
_retry:
|
||||
err.reset();
|
||||
dir_inf = GFileInfoPtr{
|
||||
g_file_query_info(dir_gfile.get(), gfile_info_query_attribs,
|
||||
G_FILE_QUERY_INFO_NONE, cancellable().get(), &err),
|
||||
false
|
||||
};
|
||||
if(!dir_inf) {
|
||||
ErrorAction act = emitError(err, ErrorSeverity::MODERATE);
|
||||
if(act == ErrorAction::RETRY) {
|
||||
err.reset();
|
||||
goto _retry;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(g_file_info_get_file_type(dir_inf.get()) != G_FILE_TYPE_DIRECTORY) {
|
||||
auto path_str = dir_path.toString();
|
||||
err = GErrorPtr{
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NOT_DIRECTORY,
|
||||
tr("The specified directory '%1' is not valid").arg(path_str.get())
|
||||
};
|
||||
emitError(err, ErrorSeverity::CRITICAL);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
dir_fi = std::make_shared<FileInfo>(dir_inf, dir_path.parent());
|
||||
}
|
||||
|
||||
FileInfoList foundFiles;
|
||||
/* check if FS is R/O and set attr. into inf */
|
||||
// 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_QUERY_INFO_NONE, cancellable().get(), &err),
|
||||
false
|
||||
};
|
||||
if(enu) {
|
||||
// qDebug() << "START LISTING:" << dir_path.toString().get();
|
||||
while(!isCancelled()) {
|
||||
err.reset();
|
||||
GFileInfoPtr inf{g_file_enumerator_next_file(enu.get(), cancellable().get(), &err), false};
|
||||
if(inf) {
|
||||
#if 0
|
||||
FmPath* dir, *sub;
|
||||
GFile* child;
|
||||
if(G_UNLIKELY(job->flags & FM_DIR_LIST_JOB_DIR_ONLY)) {
|
||||
/* FIXME: handle symlinks */
|
||||
if(g_file_info_get_file_type(inf) != G_FILE_TYPE_DIRECTORY) {
|
||||
g_object_unref(inf);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// virtual folders may return children not within them
|
||||
// For example: the search:/// URI implemented by libfm might return files from different folders during enumeration.
|
||||
// So here we call g_file_enumerator_get_container() to get the real parent path rather than simply using dir_path.
|
||||
// This is not the behaviour of gio, but the extensions by libfm might do this.
|
||||
// FIXME: after we port these vfs implementation from libfm, we can redesign this.
|
||||
FilePath realParentPath = FilePath{g_file_enumerator_get_container(enu.get()), true};
|
||||
if(isFileSearch) { // this is a file sarch job (search:/// URI)
|
||||
// FIXME: redesign file search and remove this dirty hack
|
||||
// the libfm implementation of search:/// URI returns a customized GFile implementation that does not behave normally.
|
||||
// let's get its actual URI and re-create a normal gio GFile instance from it.
|
||||
realParentPath = FilePath::fromUri(realParentPath.uri().get());
|
||||
}
|
||||
#if 0
|
||||
if(g_file_info_get_file_type(inf) == G_FILE_TYPE_DIRECTORY)
|
||||
/* for dir: check if its FS is R/O and set attr. into inf */
|
||||
{
|
||||
_fm_file_info_job_update_fs_readonly(child, inf, nullptr, nullptr);
|
||||
}
|
||||
fi = fm_file_info_new_from_g_file_data(child, inf, sub);
|
||||
#endif
|
||||
auto fileInfo = std::make_shared<FileInfo>(inf, realParentPath);
|
||||
if(emit_files_found) {
|
||||
// Q_EMIT filesFound();
|
||||
}
|
||||
|
||||
if(cutFilesHashSet_
|
||||
&& cutFilesHashSet_->count(fileInfo->path().hash()) > 0) {
|
||||
fileInfo->bindCutFiles(cutFilesHashSet_);
|
||||
}
|
||||
|
||||
foundFiles.push_back(std::move(fileInfo));
|
||||
}
|
||||
else {
|
||||
if(err) {
|
||||
ErrorAction act = emitError(err, ErrorSeverity::MILD);
|
||||
/* ErrorAction::RETRY is not supported. */
|
||||
if(act == ErrorAction::ABORT) {
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
/* otherwise it's EOL */
|
||||
break;
|
||||
}
|
||||
}
|
||||
err.reset();
|
||||
g_file_enumerator_close(enu.get(), cancellable().get(), &err);
|
||||
}
|
||||
else {
|
||||
emitError(err, ErrorSeverity::CRITICAL);
|
||||
}
|
||||
|
||||
// qDebug() << "END LISTING:" << dir_path.toString().get();
|
||||
if(!foundFiles.empty()) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
files_.swap(foundFiles);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
//FIXME: incremental..
|
||||
|
||||
static gboolean emit_found_files(gpointer user_data) {
|
||||
/* this callback is called from the main thread */
|
||||
FmDirListJob* job = FM_DIR_LIST_JOB(user_data);
|
||||
/* g_print("emit_found_files: %d\n", g_slist_length(job->files_to_add)); */
|
||||
|
||||
if(g_source_is_destroyed(g_main_current_source())) {
|
||||
return FALSE;
|
||||
}
|
||||
g_signal_emit(job, signals[FILES_FOUND], 0, job->files_to_add);
|
||||
g_slist_free_full(job->files_to_add, (GDestroyNotify)fm_file_info_unref);
|
||||
job->files_to_add = nullptr;
|
||||
job->delay_add_files_handler = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gpointer queue_add_file(FmJob* fmjob, gpointer user_data) {
|
||||
FmDirListJob* job = FM_DIR_LIST_JOB(fmjob);
|
||||
FmFileInfo* file = FM_FILE_INFO(user_data);
|
||||
/* this callback is called from the main thread */
|
||||
/* g_print("queue_add_file: %s\n", fm_file_info_get_disp_name(file)); */
|
||||
job->files_to_add = g_slist_prepend(job->files_to_add, fm_file_info_ref(file));
|
||||
if(job->delay_add_files_handler == 0)
|
||||
job->delay_add_files_handler = g_timeout_add_seconds_full(G_PRIORITY_LOW,
|
||||
1, emit_found_files, g_object_ref(job), g_object_unref);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void fm_dir_list_job_add_found_file(FmDirListJob* job, FmFileInfo* file) {
|
||||
fm_file_info_list_push_tail(job->files, file);
|
||||
if(G_UNLIKELY(job->emit_files_found)) {
|
||||
fm_job_call_main_thread(FM_JOB(job), queue_add_file, file);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Fm
|
65
src/core/dirlistjob.h
Normal file
65
src/core/dirlistjob.h
Normal file
@ -0,0 +1,65 @@
|
||||
#ifndef FM2_DIRLISTJOB_H
|
||||
#define FM2_DIRLISTJOB_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include <mutex>
|
||||
#include "job.h"
|
||||
#include "filepath.h"
|
||||
#include "gobjectptr.h"
|
||||
#include "fileinfo.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API DirListJob : public Job {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Flags {
|
||||
FAST = 0,
|
||||
DIR_ONLY = 1 << 0,
|
||||
DETAILED = 1 << 1
|
||||
};
|
||||
|
||||
explicit DirListJob(const FilePath& path, Flags flags, const std::shared_ptr<const HashSet>& cutFilesHashSet = nullptr);
|
||||
|
||||
FileInfoList& files() {
|
||||
return files_;
|
||||
}
|
||||
|
||||
void setIncremental(bool set);
|
||||
|
||||
bool incremental() const {
|
||||
return emit_files_found;
|
||||
}
|
||||
|
||||
FilePath dirPath() const {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
return dir_path;
|
||||
}
|
||||
|
||||
std::shared_ptr<const FileInfo> dirInfo() const {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
return dir_fi;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void filesFound(FileInfoList& foundFiles);
|
||||
|
||||
protected:
|
||||
|
||||
void exec() override;
|
||||
|
||||
private:
|
||||
mutable std::mutex mutex_;
|
||||
FilePath dir_path;
|
||||
Flags flags;
|
||||
std::shared_ptr<const FileInfo> dir_fi;
|
||||
FileInfoList files_;
|
||||
const std::shared_ptr<const HashSet> cutFilesHashSet_;
|
||||
bool emit_files_found;
|
||||
// guint delay_add_files_handler;
|
||||
// GSList* files_to_add;
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_DIRLISTJOB_H
|
9
src/core/filechangeattrjob.cpp
Normal file
9
src/core/filechangeattrjob.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "filechangeattrjob.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
FileChangeAttrJob::FileChangeAttrJob() {
|
||||
|
||||
}
|
||||
|
||||
} // namespace Fm
|
17
src/core/filechangeattrjob.h
Normal file
17
src/core/filechangeattrjob.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef FM2_FILECHANGEATTRJOB_H
|
||||
#define FM2_FILECHANGEATTRJOB_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include "fileoperationjob.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API FileChangeAttrJob : public Fm::FileOperationJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FileChangeAttrJob();
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_FILECHANGEATTRJOB_H
|
378
src/core/fileinfo.cpp
Normal file
378
src/core/fileinfo.cpp
Normal file
@ -0,0 +1,378 @@
|
||||
#include "fileinfo.h"
|
||||
#include "fileinfo_p.h"
|
||||
#include <gio/gio.h>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
const char gfile_info_query_attribs[] = "standard::*,"
|
||||
"unix::*,"
|
||||
"time::*,"
|
||||
"access::*,"
|
||||
"id::filesystem,"
|
||||
"metadata::emblems";
|
||||
|
||||
FileInfo::FileInfo() {
|
||||
// FIXME: initialize numeric data members
|
||||
}
|
||||
|
||||
FileInfo::FileInfo(const GFileInfoPtr& inf, const FilePath& parentDirPath) {
|
||||
setFromGFileInfo(inf, parentDirPath);
|
||||
}
|
||||
|
||||
FileInfo::~FileInfo() {
|
||||
}
|
||||
|
||||
void FileInfo::setFromGFileInfo(const GObjectPtr<GFileInfo>& inf, const FilePath& parentDirPath) {
|
||||
dirPath_ = parentDirPath;
|
||||
const char* tmp, *uri;
|
||||
GIcon* gicon;
|
||||
GFileType type;
|
||||
|
||||
name_ = g_file_info_get_name(inf.get());
|
||||
|
||||
dispName_ = g_file_info_get_display_name(inf.get());
|
||||
|
||||
size_ = g_file_info_get_size(inf.get());
|
||||
|
||||
tmp = g_file_info_get_content_type(inf.get());
|
||||
if(!tmp) {
|
||||
tmp = "application/octet-stream";
|
||||
}
|
||||
mimeType_ = MimeType::fromName(tmp);
|
||||
|
||||
mode_ = g_file_info_get_attribute_uint32(inf.get(), G_FILE_ATTRIBUTE_UNIX_MODE);
|
||||
|
||||
uid_ = gid_ = -1;
|
||||
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_UNIX_UID)) {
|
||||
uid_ = g_file_info_get_attribute_uint32(inf.get(), G_FILE_ATTRIBUTE_UNIX_UID);
|
||||
}
|
||||
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_UNIX_GID)) {
|
||||
gid_ = g_file_info_get_attribute_uint32(inf.get(), G_FILE_ATTRIBUTE_UNIX_GID);
|
||||
}
|
||||
|
||||
type = g_file_info_get_file_type(inf.get());
|
||||
if(0 == mode_) { /* if UNIX file mode is not available, compose a fake one. */
|
||||
switch(type) {
|
||||
case G_FILE_TYPE_REGULAR:
|
||||
mode_ |= S_IFREG;
|
||||
break;
|
||||
case G_FILE_TYPE_DIRECTORY:
|
||||
mode_ |= S_IFDIR;
|
||||
break;
|
||||
case G_FILE_TYPE_SYMBOLIC_LINK:
|
||||
mode_ |= S_IFLNK;
|
||||
break;
|
||||
case G_FILE_TYPE_SHORTCUT:
|
||||
break;
|
||||
case G_FILE_TYPE_MOUNTABLE:
|
||||
break;
|
||||
case G_FILE_TYPE_SPECIAL:
|
||||
if(mode_) {
|
||||
break;
|
||||
}
|
||||
/* if it's a special file but it doesn't have UNIX mode, compose a fake one. */
|
||||
if(strcmp(tmp, "inode/chardevice") == 0) {
|
||||
mode_ |= S_IFCHR;
|
||||
}
|
||||
else if(strcmp(tmp, "inode/blockdevice") == 0) {
|
||||
mode_ |= S_IFBLK;
|
||||
}
|
||||
else if(strcmp(tmp, "inode/fifo") == 0) {
|
||||
mode_ |= S_IFIFO;
|
||||
}
|
||||
#ifdef S_IFSOCK
|
||||
else if(strcmp(tmp, "inode/socket") == 0) {
|
||||
mode_ |= S_IFSOCK;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case G_FILE_TYPE_UNKNOWN:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) {
|
||||
isAccessible_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
|
||||
}
|
||||
else
|
||||
/* assume it's accessible */
|
||||
{
|
||||
isAccessible_ = true;
|
||||
}
|
||||
|
||||
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) {
|
||||
isWritable_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
|
||||
}
|
||||
else
|
||||
/* assume it's writable */
|
||||
{
|
||||
isWritable_ = true;
|
||||
}
|
||||
|
||||
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE)) {
|
||||
isDeletable_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE);
|
||||
}
|
||||
else
|
||||
/* assume it's deletable */
|
||||
{
|
||||
isDeletable_ = true;
|
||||
}
|
||||
|
||||
/* special handling for symlinks */
|
||||
if(g_file_info_get_is_symlink(inf.get())) {
|
||||
mode_ &= ~S_IFMT; /* reset type */
|
||||
mode_ |= S_IFLNK; /* set type to symlink */
|
||||
goto _file_is_symlink;
|
||||
}
|
||||
|
||||
isShortcut_ = false;
|
||||
|
||||
switch(type) {
|
||||
case G_FILE_TYPE_SHORTCUT:
|
||||
isShortcut_ = true;
|
||||
case G_FILE_TYPE_MOUNTABLE:
|
||||
uri = g_file_info_get_attribute_string(inf.get(), G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
|
||||
if(uri) {
|
||||
if(g_str_has_prefix(uri, "file:///")) {
|
||||
auto filename = CStrPtr{g_filename_from_uri(uri, nullptr, nullptr)};
|
||||
target_ = filename.get();
|
||||
}
|
||||
else {
|
||||
target_ = uri;
|
||||
}
|
||||
if(!mimeType_) {
|
||||
mimeType_ = MimeType::guessFromFileName(target_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
/* if the mime-type is not determined or is unknown */
|
||||
if(G_UNLIKELY(!mimeType_ || mimeType_->isUnknownType())) {
|
||||
/* FIXME: is this appropriate? */
|
||||
if(type == G_FILE_TYPE_SHORTCUT) {
|
||||
mimeType_ = MimeType::inodeShortcut();
|
||||
}
|
||||
else {
|
||||
mimeType_ = MimeType::inodeMountPoint();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case G_FILE_TYPE_DIRECTORY:
|
||||
if(!mimeType_) {
|
||||
mimeType_ = MimeType::inodeDirectory();
|
||||
}
|
||||
isReadOnly_ = false; /* default is R/W */
|
||||
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_FILESYSTEM_READONLY)) {
|
||||
isReadOnly_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_FILESYSTEM_READONLY);
|
||||
}
|
||||
/* directories should be writable to be deleted by user */
|
||||
if(isReadOnly_ || !isWritable_) {
|
||||
isDeletable_ = false;
|
||||
}
|
||||
break;
|
||||
case G_FILE_TYPE_SYMBOLIC_LINK:
|
||||
_file_is_symlink:
|
||||
uri = g_file_info_get_symlink_target(inf.get());
|
||||
if(uri) {
|
||||
if(g_str_has_prefix(uri, "file:///")) {
|
||||
auto filename = CStrPtr{g_filename_from_uri(uri, nullptr, nullptr)};
|
||||
target_ = filename.get();
|
||||
}
|
||||
else {
|
||||
target_ = uri;
|
||||
}
|
||||
if(!mimeType_) {
|
||||
mimeType_ = MimeType::guessFromFileName(target_.c_str());
|
||||
}
|
||||
}
|
||||
/* continue with absent mime type */
|
||||
default: /* G_FILE_TYPE_UNKNOWN G_FILE_TYPE_REGULAR G_FILE_TYPE_SPECIAL */
|
||||
if(G_UNLIKELY(!mimeType_)) {
|
||||
if(!mimeType_) {
|
||||
mimeType_ = MimeType::guessFromFileName(name_.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if there is a custom folder icon, use it */
|
||||
if(isNative() && type == G_FILE_TYPE_DIRECTORY) {
|
||||
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) {
|
||||
auto dot_icon = IconInfo::fromName(icon_name.get());
|
||||
if(dot_icon && dot_icon->isValid()) {
|
||||
icon_ = dot_icon;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_key_file_free(kf);
|
||||
}
|
||||
}
|
||||
|
||||
if(!icon_) {
|
||||
/* try file-specific icon first */
|
||||
gicon = g_file_info_get_icon(inf.get());
|
||||
if(gicon) {
|
||||
icon_ = IconInfo::fromGIcon(gicon);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* set "locked" icon on unaccesible folder */
|
||||
else if(!accessible && type == G_FILE_TYPE_DIRECTORY) {
|
||||
icon = g_object_ref(icon_locked_folder);
|
||||
}
|
||||
else {
|
||||
icon = g_object_ref(fm_mime_type_get_icon(mime_type));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* if the file has emblems, add them to the icon */
|
||||
auto emblem_names = g_file_info_get_attribute_stringv(inf.get(), "metadata::emblems");
|
||||
if(emblem_names) {
|
||||
auto n_emblems = g_strv_length(emblem_names);
|
||||
for(int i = n_emblems - 1; i >= 0; --i) {
|
||||
emblems_.emplace_front(Fm::IconInfo::fromName(emblem_names[i]));
|
||||
}
|
||||
}
|
||||
|
||||
tmp = g_file_info_get_attribute_string(inf.get(), G_FILE_ATTRIBUTE_ID_FILESYSTEM);
|
||||
filesystemId_ = g_intern_string(tmp);
|
||||
|
||||
mtime_ = g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_TIME_MODIFIED);
|
||||
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());
|
||||
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)) {
|
||||
isNameChangeable_ = g_file_info_get_attribute_boolean(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME);
|
||||
}
|
||||
|
||||
// special handling for desktop entry files (show the name and icon defined in the desktop entry instead)
|
||||
if(isNative() && G_UNLIKELY(isDesktopEntry())) {
|
||||
auto local_path = path().localPath();
|
||||
GKeyFile* kf = g_key_file_new();
|
||||
if(g_key_file_load_from_file(kf, local_path.get(), G_KEY_FILE_NONE, nullptr)) {
|
||||
/* check if type is correct and supported */
|
||||
CStrPtr type{g_key_file_get_string(kf, "Desktop Entry", "Type", nullptr)};
|
||||
if(type) {
|
||||
// Type == "Link"
|
||||
if(strcmp(type.get(), G_KEY_FILE_DESKTOP_TYPE_LINK) == 0) {
|
||||
CStrPtr uri{g_key_file_get_string(kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_URL, nullptr)};
|
||||
if(uri) {
|
||||
isShortcut_ = true;
|
||||
target_ = uri.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
CStrPtr icon_name{g_key_file_get_string(kf, "Desktop Entry", "Icon", nullptr)};
|
||||
if(icon_name) {
|
||||
icon_ = IconInfo::fromName(icon_name.get());
|
||||
}
|
||||
/* Use title of the desktop entry for display */
|
||||
CStrPtr displayName{g_key_file_get_locale_string(kf, "Desktop Entry", "Name", nullptr, nullptr)};
|
||||
if(displayName) {
|
||||
dispName_ = displayName.get();
|
||||
}
|
||||
/* handle 'Hidden' key to set hidden attribute */
|
||||
if(!isHidden_) {
|
||||
isHidden_ = g_key_file_get_boolean(kf, "Desktop Entry", "Hidden", nullptr);
|
||||
}
|
||||
}
|
||||
g_key_file_free(kf);
|
||||
}
|
||||
|
||||
if(!icon_ && mimeType_)
|
||||
icon_ = mimeType_->icon();
|
||||
|
||||
#if 0
|
||||
GFile* _gf = nullptr;
|
||||
GFileAttributeInfoList* list;
|
||||
auto list = g_file_query_settable_attributes(gf, nullptr, nullptr);
|
||||
if(G_LIKELY(list)) {
|
||||
if(g_file_attribute_info_list_lookup(list, G_FILE_ATTRIBUTE_STANDARD_ICON)) {
|
||||
icon_is_changeable = true;
|
||||
}
|
||||
if(g_file_attribute_info_list_lookup(list, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN)) {
|
||||
hidden_is_changeable = true;
|
||||
}
|
||||
g_file_attribute_info_list_unref(list);
|
||||
}
|
||||
if(G_UNLIKELY(_gf)) {
|
||||
g_object_unref(_gf);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FileInfo::bindCutFiles(const std::shared_ptr<const HashSet>& cutFilesHashSet) {
|
||||
cutFilesHashSet_ = cutFilesHashSet;
|
||||
}
|
||||
|
||||
bool FileInfo::canThumbnail() const {
|
||||
/* We cannot use S_ISREG here as this exclude all symlinks */
|
||||
if(size_ == 0 || /* don't generate thumbnails for empty files */
|
||||
!(mode_ & S_IFREG) ||
|
||||
isDesktopEntry() ||
|
||||
isUnknownType()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
/* 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 */
|
||||
auto pathStr = path().toString();
|
||||
int fd = open(pathStr.get(), O_RDONLY);
|
||||
if(fd >= 0) {
|
||||
char buf[2];
|
||||
ssize_t rdlen = read(fd, &buf, 2);
|
||||
close(fd);
|
||||
if(rdlen == 2 && buf[0] == '#' && buf[1] == '!') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return mimeType_->canBeExecutable();
|
||||
}
|
||||
|
||||
|
||||
bool FileInfoList::isSameType() const {
|
||||
if(!empty()) {
|
||||
auto& item = front();
|
||||
for(auto it = cbegin() + 1; it != cend(); ++it) {
|
||||
auto& item2 = *it;
|
||||
if(item->mimeType() != item2->mimeType()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileInfoList::isSameFilesystem() const {
|
||||
if(!empty()) {
|
||||
auto& item = front();
|
||||
for(auto it = cbegin() + 1; it != cend(); ++it) {
|
||||
auto& item2 = *it;
|
||||
if(item->filesystemId() != item2->filesystemId()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace Fm
|
278
src/core/fileinfo.h
Normal file
278
src/core/fileinfo.h
Normal file
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* 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_FM2_FILE_INFO_H__
|
||||
#define __LIBFM_QT_FM2_FILE_INFO_H__
|
||||
|
||||
#include <libfm/fm.h>
|
||||
#include <QObject>
|
||||
#include <QtGlobal>
|
||||
#include "../libfmqtglobals.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <forward_list>
|
||||
|
||||
#include "gioptrs.h"
|
||||
#include "filepath.h"
|
||||
#include "iconinfo.h"
|
||||
#include "mimetype.h"
|
||||
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class FileInfoList;
|
||||
typedef std::set<unsigned int> HashSet;
|
||||
|
||||
class LIBFM_QT_API FileInfo {
|
||||
public:
|
||||
|
||||
explicit FileInfo();
|
||||
|
||||
explicit FileInfo(const GFileInfoPtr& inf, const FilePath& parentDirPath);
|
||||
|
||||
virtual ~FileInfo();
|
||||
|
||||
bool canSetHidden() const {
|
||||
return isHiddenChangeable_;
|
||||
}
|
||||
|
||||
bool canSetIcon() const {
|
||||
return isIconChangeable_;
|
||||
}
|
||||
|
||||
bool canSetName() const {
|
||||
return isNameChangeable_;
|
||||
}
|
||||
|
||||
bool canThumbnail() const;
|
||||
|
||||
gid_t gid() const {
|
||||
return gid_;
|
||||
}
|
||||
|
||||
uid_t uid() const {
|
||||
return uid_;
|
||||
}
|
||||
|
||||
const char* filesystemId() const {
|
||||
return filesystemId_;
|
||||
}
|
||||
|
||||
const std::shared_ptr<const IconInfo>& icon() const {
|
||||
return icon_;
|
||||
}
|
||||
|
||||
const std::shared_ptr<const MimeType>& mimeType() const {
|
||||
return mimeType_;
|
||||
}
|
||||
|
||||
time_t ctime() const {
|
||||
return ctime_;
|
||||
}
|
||||
|
||||
|
||||
time_t atime() const {
|
||||
return atime_;
|
||||
}
|
||||
|
||||
time_t mtime() const {
|
||||
return mtime_;
|
||||
}
|
||||
|
||||
const std::string& target() const {
|
||||
return target_;
|
||||
}
|
||||
|
||||
bool isWritableDirectory() const {
|
||||
return (!isReadOnly_ && isDir());
|
||||
}
|
||||
|
||||
bool isAccessible() const {
|
||||
return isAccessible_;
|
||||
}
|
||||
|
||||
bool isWritable() const {
|
||||
return isWritable_;
|
||||
}
|
||||
|
||||
bool isDeletable() const {
|
||||
return isDeletable_;
|
||||
}
|
||||
|
||||
bool isExecutableType() const;
|
||||
|
||||
bool isBackup() const {
|
||||
return isBackup_;
|
||||
}
|
||||
|
||||
bool isHidden() const {
|
||||
// FIXME: we might treat backup files as hidden
|
||||
return isHidden_;
|
||||
}
|
||||
|
||||
bool isUnknownType() const {
|
||||
return mimeType_->isUnknownType();
|
||||
}
|
||||
|
||||
bool isDesktopEntry() const {
|
||||
return mimeType_->isDesktopEntry();
|
||||
}
|
||||
|
||||
bool isText() const {
|
||||
return mimeType_->isText();
|
||||
}
|
||||
|
||||
bool isImage() const {
|
||||
return mimeType_->isImage();
|
||||
}
|
||||
|
||||
bool isMountable() const {
|
||||
return mimeType_->isMountable();
|
||||
}
|
||||
|
||||
bool isShortcut() const {
|
||||
return isShortcut_;
|
||||
}
|
||||
|
||||
bool isSymlink() const {
|
||||
return S_ISLNK(mode_) ? true : false;
|
||||
}
|
||||
|
||||
bool isDir() const {
|
||||
return mimeType_->isDir();
|
||||
}
|
||||
|
||||
bool isNative() const {
|
||||
return dirPath_ ? dirPath_.isNative() : path().isNative();
|
||||
}
|
||||
|
||||
bool isCut() const {
|
||||
return !cutFilesHashSet_.expired();
|
||||
}
|
||||
|
||||
mode_t mode() const {
|
||||
return mode_;
|
||||
}
|
||||
|
||||
uint64_t realSize() const {
|
||||
return blksize_ *blocks_;
|
||||
}
|
||||
|
||||
uint64_t size() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
const std::string& name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
const QString& displayName() const {
|
||||
return dispName_;
|
||||
}
|
||||
|
||||
FilePath path() const {
|
||||
return dirPath_ ? dirPath_.child(name_.c_str()) : FilePath::fromPathStr(name_.c_str());
|
||||
}
|
||||
|
||||
const FilePath& dirPath() const {
|
||||
return dirPath_;
|
||||
}
|
||||
|
||||
void setFromGFileInfo(const GFileInfoPtr& inf, const FilePath& parentDirPath);
|
||||
|
||||
void bindCutFiles(const std::shared_ptr<const HashSet>& cutFilesHashSet);
|
||||
|
||||
const std::forward_list<std::shared_ptr<const IconInfo>>& emblems() const {
|
||||
return emblems_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
QString dispName_;
|
||||
|
||||
FilePath dirPath_;
|
||||
|
||||
mode_t mode_;
|
||||
const char* filesystemId_;
|
||||
uid_t uid_;
|
||||
gid_t gid_;
|
||||
uint64_t size_;
|
||||
time_t mtime_;
|
||||
time_t atime_;
|
||||
time_t ctime_;
|
||||
|
||||
uint64_t blksize_;
|
||||
uint64_t blocks_;
|
||||
|
||||
std::shared_ptr<const MimeType> mimeType_;
|
||||
std::shared_ptr<const IconInfo> icon_;
|
||||
std::forward_list<std::shared_ptr<const IconInfo>> emblems_;
|
||||
|
||||
std::string target_; /* target of shortcut or mountable. */
|
||||
|
||||
bool isShortcut_ : 1; /* TRUE if file is shortcut type */
|
||||
bool isAccessible_ : 1; /* TRUE if can be read by user */
|
||||
bool isWritable_ : 1; /* TRUE if can be written to by user */
|
||||
bool isDeletable_ : 1; /* TRUE if can be deleted by user */
|
||||
bool isHidden_ : 1; /* TRUE if file is hidden */
|
||||
bool isBackup_ : 1; /* TRUE if file is backup */
|
||||
bool isNameChangeable_ : 1; /* TRUE if name can be changed */
|
||||
bool isIconChangeable_ : 1; /* TRUE if icon can be changed */
|
||||
bool isHiddenChangeable_ : 1; /* TRUE if hidden can be changed */
|
||||
bool isReadOnly_ : 1; /* TRUE if host FS is R/O */
|
||||
|
||||
std::weak_ptr<const HashSet> cutFilesHashSet_;
|
||||
// std::vector<std::tuple<int, void*, void(void*)>> extraData_;
|
||||
};
|
||||
|
||||
|
||||
class LIBFM_QT_API FileInfoList: public std::vector<std::shared_ptr<const FileInfo>> {
|
||||
public:
|
||||
|
||||
bool isSameType() const;
|
||||
|
||||
bool isSameFilesystem() const;
|
||||
|
||||
FilePathList paths() const {
|
||||
FilePathList ret;
|
||||
for(auto& file: *this) {
|
||||
ret.push_back(file->path());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
typedef std::pair<std::shared_ptr<const FileInfo>, std::shared_ptr<const FileInfo>> FileInfoPair;
|
||||
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(std::shared_ptr<const Fm::FileInfo>)
|
||||
|
||||
|
||||
#endif // __LIBFM_QT_FM2_FILE_INFO_H__
|
10
src/core/fileinfo_p.h
Normal file
10
src/core/fileinfo_p.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef FILEINFO_P_H
|
||||
#define FILEINFO_P_H
|
||||
|
||||
namespace Fm {
|
||||
|
||||
extern const char gfile_info_query_attribs[];
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FILEINFO_P_H
|
42
src/core/fileinfojob.cpp
Normal file
42
src/core/fileinfojob.cpp
Normal file
@ -0,0 +1,42 @@
|
||||
#include "fileinfojob.h"
|
||||
#include "fileinfo_p.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
FileInfoJob::FileInfoJob(FilePathList paths, FilePath commonDirPath, const std::shared_ptr<const HashSet>& cutFilesHashSet):
|
||||
Job(),
|
||||
paths_{std::move(paths)},
|
||||
commonDirPath_{std::move(commonDirPath)},
|
||||
cutFilesHashSet_{cutFilesHashSet} {
|
||||
}
|
||||
|
||||
void FileInfoJob::exec() {
|
||||
for(const auto& path: paths_) {
|
||||
if(!isCancelled()) {
|
||||
GErrorPtr err;
|
||||
GFileInfoPtr inf{
|
||||
g_file_query_info(path.gfile().get(), gfile_info_query_attribs,
|
||||
G_FILE_QUERY_INFO_NONE, cancellable().get(), &err),
|
||||
false
|
||||
};
|
||||
if(!inf)
|
||||
return;
|
||||
|
||||
// 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();
|
||||
FileInfo fileInfo(inf, dirPath);
|
||||
|
||||
if(cutFilesHashSet_
|
||||
&& cutFilesHashSet_->count(fileInfo.path().hash())) {
|
||||
fileInfo.bindCutFiles(cutFilesHashSet_);
|
||||
}
|
||||
|
||||
auto fileInfoPtr = std::make_shared<const FileInfo>(fileInfo);
|
||||
|
||||
results_.push_back(fileInfoPtr);
|
||||
Q_EMIT gotInfo(path, fileInfoPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Fm
|
41
src/core/fileinfojob.h
Normal file
41
src/core/fileinfojob.h
Normal file
@ -0,0 +1,41 @@
|
||||
#ifndef FM2_FILEINFOJOB_H
|
||||
#define FM2_FILEINFOJOB_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include "job.h"
|
||||
#include "filepath.h"
|
||||
#include "fileinfo.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
|
||||
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);
|
||||
|
||||
const FilePathList& paths() const {
|
||||
return paths_;
|
||||
}
|
||||
|
||||
const FileInfoList& files() const {
|
||||
return results_;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void gotInfo(const FilePath& path, std::shared_ptr<const FileInfo>& info);
|
||||
|
||||
protected:
|
||||
void exec() override;
|
||||
|
||||
private:
|
||||
FilePathList paths_;
|
||||
FileInfoList results_;
|
||||
FilePath commonDirPath_;
|
||||
const std::shared_ptr<const HashSet> cutFilesHashSet_;
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_FILEINFOJOB_H
|
9
src/core/filelinkjob.cpp
Normal file
9
src/core/filelinkjob.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "filelinkjob.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
FileLinkJob::FileLinkJob() {
|
||||
|
||||
}
|
||||
|
||||
} // namespace Fm
|
16
src/core/filelinkjob.h
Normal file
16
src/core/filelinkjob.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef FM2_FILELINKJOB_H
|
||||
#define FM2_FILELINKJOB_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include "fileoperationjob.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API FileLinkJob : public Fm::FileOperationJob {
|
||||
public:
|
||||
explicit FileLinkJob();
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_FILELINKJOB_H
|
9
src/core/filemonitor.cpp
Normal file
9
src/core/filemonitor.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
#include "filemonitor.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
FileMonitor::FileMonitor() {
|
||||
|
||||
}
|
||||
|
||||
} // namespace Fm
|
26
src/core/filemonitor.h
Normal file
26
src/core/filemonitor.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef FM2_FILEMONITOR_H
|
||||
#define FM2_FILEMONITOR_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include <QObject>
|
||||
#include "gioptrs.h"
|
||||
#include "filepath.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API FileMonitor: public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
explicit FileMonitor();
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
|
||||
private:
|
||||
GFileMonitorPtr monitor_;
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_FILEMONITOR_H
|
79
src/core/fileoperationjob.cpp
Normal file
79
src/core/fileoperationjob.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include "fileoperationjob.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
FileOperationJob::FileOperationJob():
|
||||
hasTotalAmount_{false},
|
||||
totalSize_{0},
|
||||
totalCount_{0},
|
||||
finishedSize_{0},
|
||||
finishedCount_{0},
|
||||
currentFileSize_{0},
|
||||
currentFileFinished_{0} {
|
||||
}
|
||||
|
||||
bool FileOperationJob::totalAmount(uint64_t& fileSize, uint64_t& fileCount) const {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
if(hasTotalAmount_) {
|
||||
fileSize = totalSize_;
|
||||
fileCount = totalCount_;
|
||||
}
|
||||
return hasTotalAmount_;
|
||||
}
|
||||
|
||||
bool FileOperationJob::currentFileProgress(FilePath& path, uint64_t& totalSize, uint64_t& finishedSize) const {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
if(currentFile_.isValid()) {
|
||||
path = currentFile_;
|
||||
totalSize = currentFileSize_;
|
||||
finishedSize = currentFileFinished_;
|
||||
}
|
||||
return currentFile_.isValid();
|
||||
}
|
||||
|
||||
FileOperationJob::FileExistsAction FileOperationJob::askRename(const FileInfo &src, const FileInfo &dest, FilePath &newDest) {
|
||||
FileExistsAction action = SKIP;
|
||||
Q_EMIT fileExists(src, dest, action, newDest);
|
||||
return action;
|
||||
}
|
||||
|
||||
bool FileOperationJob::finishedAmount(uint64_t& finishedSize, uint64_t& finishedCount) const {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
if(hasTotalAmount_) {
|
||||
finishedSize = finishedSize_;
|
||||
finishedCount = finishedCount_;
|
||||
}
|
||||
return hasTotalAmount_;
|
||||
}
|
||||
|
||||
void FileOperationJob::setTotalAmount(uint64_t fileSize, uint64_t fileCount) {
|
||||
std::lock_guard<std::mutex> locl{mutex_};
|
||||
hasTotalAmount_ = true;
|
||||
totalSize_ = fileSize;
|
||||
totalCount_ = fileCount;
|
||||
}
|
||||
|
||||
void FileOperationJob::setFinishedAmount(uint64_t finishedSize, uint64_t finishedCount) {
|
||||
std::lock_guard<std::mutex> locl{mutex_};
|
||||
finishedSize_ = finishedSize;
|
||||
finishedCount_ = finishedCount;
|
||||
}
|
||||
|
||||
void FileOperationJob::addFinishedAmount(uint64_t finishedSize, uint64_t finishedCount) {
|
||||
std::lock_guard<std::mutex> locl{mutex_};
|
||||
finishedSize_ += finishedSize;
|
||||
finishedCount_ += finishedCount;
|
||||
}
|
||||
|
||||
void FileOperationJob::setCurrentFile(const FilePath& path) {
|
||||
std::lock_guard<std::mutex> locl{mutex_};
|
||||
currentFile_ = path;
|
||||
}
|
||||
|
||||
void FileOperationJob::setCurrentFileProgress(uint64_t totalSize, uint64_t finishedSize) {
|
||||
std::lock_guard<std::mutex> locl{mutex_};
|
||||
currentFileSize_ = totalSize;
|
||||
currentFileFinished_ = finishedSize;
|
||||
}
|
||||
|
||||
} // namespace Fm
|
73
src/core/fileoperationjob.h
Normal file
73
src/core/fileoperationjob.h
Normal file
@ -0,0 +1,73 @@
|
||||
#ifndef FM2_FILEOPERATIONJOB_H
|
||||
#define FM2_FILEOPERATIONJOB_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include "job.h"
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <cstdint>
|
||||
#include "fileinfo.h"
|
||||
#include "filepath.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API FileOperationJob : public Fm::Job {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum FileExistsAction {
|
||||
CANCEL = 0,
|
||||
OVERWRITE = 1<<0,
|
||||
RENAME = 1<<1,
|
||||
SKIP = 1<<2,
|
||||
SKIP_ERROR = 1<<3
|
||||
};
|
||||
|
||||
explicit FileOperationJob();
|
||||
|
||||
bool totalAmount(std::uint64_t& fileSize, std::uint64_t& fileCount) const;
|
||||
|
||||
bool finishedAmount(std::uint64_t& finishedSize, std::uint64_t& finishedCount) const;
|
||||
|
||||
bool currentFileProgress(FilePath& path, std::uint64_t& totalSize, std::uint64_t& finishedSize) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
void preparedToRun();
|
||||
|
||||
// void currentFile(const char* file);
|
||||
|
||||
// void progress(uint32_t percent);
|
||||
|
||||
// to correctly handle the signal, connect with Qt::BlockingQueuedConnection so it's a sync call.
|
||||
void fileExists(const FileInfo& src, const FileInfo& dest, FileExistsAction& response, FilePath& newDest);
|
||||
|
||||
protected:
|
||||
|
||||
FileExistsAction askRename(const FileInfo& src, const FileInfo& dest, FilePath& newDest);
|
||||
|
||||
void setTotalAmount(std::uint64_t fileSize, std::uint64_t fileCount);
|
||||
|
||||
void setFinishedAmount(std::uint64_t finishedSize, std::uint64_t finishedCount);
|
||||
|
||||
void addFinishedAmount(std::uint64_t finishedSize, std::uint64_t finishedCount);
|
||||
|
||||
void setCurrentFile(const FilePath &path);
|
||||
|
||||
void setCurrentFileProgress(uint64_t totalSize, uint64_t finishedSize);
|
||||
|
||||
private:
|
||||
bool hasTotalAmount_;
|
||||
std::uint64_t totalSize_;
|
||||
std::uint64_t totalCount_;
|
||||
std::uint64_t finishedSize_;
|
||||
std::uint64_t finishedCount_;
|
||||
|
||||
FilePath currentFile_;
|
||||
std::uint64_t currentFileSize_;
|
||||
std::uint64_t currentFileFinished_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_FILEOPERATIONJOB_H
|
21
src/core/filepath.cpp
Normal file
21
src/core/filepath.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "filepath.h"
|
||||
#include <cstdlib>
|
||||
#include <utility>
|
||||
#include <glib.h>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
FilePath FilePath::homeDir_;
|
||||
|
||||
const FilePath &FilePath::homeDir() {
|
||||
if(!homeDir_) {
|
||||
const char* home = getenv("HOME");
|
||||
if(!home) {
|
||||
home = g_get_home_dir();
|
||||
}
|
||||
homeDir_ = FilePath::fromLocalPath(home);
|
||||
}
|
||||
return homeDir_;
|
||||
}
|
||||
|
||||
} // namespace Fm
|
177
src/core/filepath.h
Normal file
177
src/core/filepath.h
Normal file
@ -0,0 +1,177 @@
|
||||
#ifndef FM2_FILEPATH_H
|
||||
#define FM2_FILEPATH_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include "gobjectptr.h"
|
||||
#include "cstrptr.h"
|
||||
#include <gio/gio.h>
|
||||
#include <vector>
|
||||
#include <QMetaType>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API FilePath {
|
||||
public:
|
||||
|
||||
explicit FilePath() {
|
||||
}
|
||||
|
||||
explicit FilePath(GFile* gfile, bool add_ref): gfile_{gfile, add_ref} {
|
||||
}
|
||||
|
||||
FilePath(const FilePath& other): FilePath{} {
|
||||
*this = other;
|
||||
}
|
||||
|
||||
FilePath(FilePath&& other) noexcept: FilePath{} {
|
||||
*this = other;
|
||||
}
|
||||
|
||||
static FilePath fromUri(const char* uri) {
|
||||
return FilePath{g_file_new_for_uri(uri), false};
|
||||
}
|
||||
|
||||
static FilePath fromLocalPath(const char* path) {
|
||||
return FilePath{g_file_new_for_path(path), false};
|
||||
}
|
||||
|
||||
static FilePath fromDisplayName(const char* path) {
|
||||
return FilePath{g_file_parse_name(path), false};
|
||||
}
|
||||
|
||||
static FilePath fromPathStr(const char* path_str) {
|
||||
return FilePath{g_file_new_for_commandline_arg(path_str), false};
|
||||
}
|
||||
|
||||
bool isValid() const {
|
||||
return gfile_ != nullptr;
|
||||
}
|
||||
|
||||
unsigned int hash() const {
|
||||
return g_file_hash(gfile_.get());
|
||||
}
|
||||
|
||||
CStrPtr baseName() const {
|
||||
return CStrPtr{g_file_get_basename(gfile_.get())};
|
||||
}
|
||||
|
||||
CStrPtr localPath() const {
|
||||
return CStrPtr{g_file_get_path(gfile_.get())};
|
||||
}
|
||||
|
||||
CStrPtr uri() const {
|
||||
return CStrPtr{g_file_get_uri(gfile_.get())};
|
||||
}
|
||||
|
||||
CStrPtr toString() const {
|
||||
if(isNative()) {
|
||||
return localPath();
|
||||
}
|
||||
return uri();
|
||||
}
|
||||
|
||||
// a human readable UTF-8 display name for the path
|
||||
CStrPtr displayName() const {
|
||||
return CStrPtr{g_file_get_parse_name(gfile_.get())};
|
||||
}
|
||||
|
||||
FilePath parent() const {
|
||||
return FilePath{g_file_get_parent(gfile_.get()), false};
|
||||
}
|
||||
|
||||
bool hasParent() const {
|
||||
return g_file_has_parent(gfile_.get(), nullptr);
|
||||
}
|
||||
|
||||
bool isParentOf(const FilePath& other) {
|
||||
return g_file_has_parent(other.gfile_.get(), gfile_.get());
|
||||
}
|
||||
|
||||
bool isPrefixOf(const FilePath& other) {
|
||||
return g_file_has_prefix(other.gfile_.get(), gfile_.get());
|
||||
}
|
||||
|
||||
FilePath child(const char* name) const {
|
||||
return FilePath{g_file_get_child(gfile_.get(), name), false};
|
||||
}
|
||||
|
||||
CStrPtr relativePathStr(const FilePath& descendant) const {
|
||||
return CStrPtr{g_file_get_relative_path(gfile_.get(), descendant.gfile_.get())};
|
||||
}
|
||||
|
||||
FilePath relativePath(const char* relPath) const {
|
||||
return FilePath{g_file_resolve_relative_path(gfile_.get(), relPath), false};
|
||||
}
|
||||
|
||||
bool isNative() const {
|
||||
return g_file_is_native(gfile_.get());
|
||||
}
|
||||
|
||||
bool hasUriScheme(const char* scheme) const {
|
||||
return g_file_has_uri_scheme(gfile_.get(), scheme);
|
||||
}
|
||||
|
||||
CStrPtr uriScheme() const {
|
||||
return CStrPtr{g_file_get_uri_scheme(gfile_.get())};
|
||||
}
|
||||
|
||||
const GObjectPtr<GFile>& gfile() const {
|
||||
return gfile_;
|
||||
}
|
||||
|
||||
FilePath& operator = (const FilePath& other) {
|
||||
gfile_ = other.gfile_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FilePath& operator = (const FilePath&& other) noexcept {
|
||||
gfile_ = std::move(other.gfile_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator == (const FilePath& other) const {
|
||||
return operator==(other.gfile_.get());
|
||||
}
|
||||
|
||||
bool operator == (GFile* other_gfile) const {
|
||||
if(gfile_ == other_gfile) {
|
||||
return true;
|
||||
}
|
||||
if(gfile_ && other_gfile) {
|
||||
return g_file_equal(gfile_.get(), other_gfile);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator != (const FilePath& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
bool operator != (std::nullptr_t) const {
|
||||
return gfile_ != nullptr;
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return gfile_ != nullptr;
|
||||
}
|
||||
|
||||
static const FilePath& homeDir();
|
||||
|
||||
private:
|
||||
GObjectPtr<GFile> gfile_;
|
||||
static FilePath homeDir_;
|
||||
};
|
||||
|
||||
struct FilePathHash {
|
||||
std::size_t operator() (const FilePath& path) const {
|
||||
return path.hash();
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::vector<FilePath> FilePathList;
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
Q_DECLARE_METATYPE(Fm::FilePath)
|
||||
|
||||
#endif // FM2_FILEPATH_H
|
24
src/core/filesysteminfojob.cpp
Normal file
24
src/core/filesysteminfojob.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "filesysteminfojob.h"
|
||||
#include "gobjectptr.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
void FileSystemInfoJob::exec() {
|
||||
GObjectPtr<GFileInfo> inf = GObjectPtr<GFileInfo>{
|
||||
g_file_query_filesystem_info(
|
||||
path_.gfile().get(),
|
||||
G_FILE_ATTRIBUTE_FILESYSTEM_SIZE","
|
||||
G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
|
||||
cancellable().get(), nullptr),
|
||||
false
|
||||
};
|
||||
if(!inf)
|
||||
return;
|
||||
if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_FILESYSTEM_SIZE)) {
|
||||
size_ = g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_FILESYSTEM_SIZE);
|
||||
freeSize_ = g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
|
||||
isAvailable_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Fm
|
45
src/core/filesysteminfojob.h
Normal file
45
src/core/filesysteminfojob.h
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef FM2_FILESYSTEMINFOJOB_H
|
||||
#define FM2_FILESYSTEMINFOJOB_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include "job.h"
|
||||
#include "filepath.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API FileSystemInfoJob : public Job {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FileSystemInfoJob(const FilePath& path):
|
||||
path_{path},
|
||||
isAvailable_{false},
|
||||
size_{0},
|
||||
freeSize_{0} {
|
||||
}
|
||||
|
||||
bool isAvailable() const {
|
||||
return isAvailable_;
|
||||
}
|
||||
|
||||
uint64_t size() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
uint64_t freeSize() const {
|
||||
return freeSize_;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void exec() override;
|
||||
|
||||
private:
|
||||
FilePath path_;
|
||||
bool isAvailable_;
|
||||
uint64_t size_;
|
||||
uint64_t freeSize_;
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_FILESYSTEMINFOJOB_H
|
908
src/core/folder.cpp
Normal file
908
src/core/folder.cpp
Normal file
@ -0,0 +1,908 @@
|
||||
/*
|
||||
* fm-folder.c
|
||||
*
|
||||
* Copyright 2009 - 2012 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
||||
* Copyright 2012-2016 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
|
||||
*
|
||||
* This file is a part of the Libfm library.
|
||||
*
|
||||
* 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 "folder.h"
|
||||
#include <string.h>
|
||||
#include <cassert>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
|
||||
#include "dirlistjob.h"
|
||||
#include "filesysteminfojob.h"
|
||||
#include "fileinfojob.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
std::unordered_map<FilePath, std::weak_ptr<Folder>, FilePathHash> Folder::cache_;
|
||||
FilePath Folder::cutFilesDirPath_;
|
||||
FilePath Folder::lastCutFilesDirPath_;
|
||||
std::shared_ptr<const HashSet> Folder::cutFilesHashSet_;
|
||||
std::mutex Folder::mutex_;
|
||||
|
||||
Folder::Folder():
|
||||
dirlist_job{nullptr},
|
||||
fsInfoJob_{nullptr},
|
||||
volumeManager_{VolumeManager::globalInstance()},
|
||||
/* for file monitor */
|
||||
has_idle_reload_handler{0},
|
||||
has_idle_update_handler{false},
|
||||
pending_change_notify{false},
|
||||
filesystem_info_pending{false},
|
||||
wants_incremental{false},
|
||||
stop_emission{false}, /* don't set it 1 bit to not lock other bits */
|
||||
/* filesystem info - set in query thread, read in main */
|
||||
fs_total_size{0},
|
||||
fs_free_size{0},
|
||||
has_fs_info{false},
|
||||
defer_content_test{false} {
|
||||
|
||||
connect(volumeManager_.get(), &VolumeManager::mountAdded, this, &Folder::onMountAdded);
|
||||
connect(volumeManager_.get(), &VolumeManager::mountRemoved, this, &Folder::onMountRemoved);
|
||||
}
|
||||
|
||||
Folder::Folder(const FilePath& path): Folder() {
|
||||
dirPath_ = path;
|
||||
}
|
||||
|
||||
Folder::~Folder() {
|
||||
if(dirMonitor_) {
|
||||
g_signal_handlers_disconnect_by_data(dirMonitor_.get(), this);
|
||||
dirMonitor_.reset();
|
||||
}
|
||||
|
||||
if(dirlist_job) {
|
||||
dirlist_job->cancel();
|
||||
}
|
||||
|
||||
// cancel any file info job in progress.
|
||||
for(auto job: fileinfoJobs_) {
|
||||
job->cancel();
|
||||
}
|
||||
|
||||
if(fsInfoJob_) {
|
||||
fsInfoJob_->cancel();
|
||||
}
|
||||
|
||||
// We store a weak_ptr instead of shared_ptr in the hash table, so the hash table
|
||||
// does not own a reference to the folder. When the last reference to Folder is
|
||||
// freed, we need to remove its hash table entry.
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
auto it = cache_.find(dirPath_);
|
||||
if(it != cache_.end()) {
|
||||
cache_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
std::shared_ptr<Folder> Folder::fromPath(const FilePath& path) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
auto it = cache_.find(path);
|
||||
if(it != cache_.end()) {
|
||||
auto folder = it->second.lock();
|
||||
if(folder) {
|
||||
return folder;
|
||||
}
|
||||
else { // FIXME: is this possible?
|
||||
cache_.erase(it);
|
||||
}
|
||||
}
|
||||
auto folder = std::make_shared<Folder>(path);
|
||||
folder->reload();
|
||||
cache_.emplace(path, folder);
|
||||
return folder;
|
||||
}
|
||||
|
||||
bool Folder::makeDirectory(const char* /*name*/, GError** /*error*/) {
|
||||
// TODO:
|
||||
// FIXME: what the API is used for in the original libfm C API?
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Folder::isIncremental() const {
|
||||
return wants_incremental;
|
||||
}
|
||||
|
||||
bool Folder::isValid() const {
|
||||
return dirInfo_ != nullptr;
|
||||
}
|
||||
|
||||
bool Folder::isLoaded() const {
|
||||
return (dirlist_job == nullptr);
|
||||
}
|
||||
|
||||
std::shared_ptr<const FileInfo> Folder::fileByName(const char* name) const {
|
||||
auto it = files_.find(name);
|
||||
if(it != files_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Folder::isEmpty() const {
|
||||
return files_.empty();
|
||||
}
|
||||
|
||||
FileInfoList Folder::files() const {
|
||||
FileInfoList ret;
|
||||
ret.reserve(files_.size());
|
||||
for(const auto& item : files_) {
|
||||
ret.push_back(item.second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
const FilePath& Folder::path() const {
|
||||
auto pathStr = dirPath_.toString();
|
||||
// qDebug() << this << "FOLDER_PATH:" << pathStr.get() << dirPath_.gfile().get();
|
||||
//assert(!g_str_has_prefix(pathStr.get(), "file:"));
|
||||
return dirPath_;
|
||||
}
|
||||
|
||||
const std::shared_ptr<const FileInfo>& Folder::info() const {
|
||||
return dirInfo_;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void Folder::init(FmFolder* folder) {
|
||||
files = fm_file_info_list_new();
|
||||
G_LOCK(hash);
|
||||
if(G_UNLIKELY(hash_uses == 0)) {
|
||||
hash = g_hash_table_new((GHashFunc)fm_path_hash, (GEqualFunc)fm_path_equal);
|
||||
volume_monitor = g_volume_monitor_get();
|
||||
if(G_LIKELY(volume_monitor)) {
|
||||
g_signal_connect(volume_monitor, "mount-added", G_CALLBACK(on_mount_added), nullptr);
|
||||
g_signal_connect(volume_monitor, "mount-removed", G_CALLBACK(on_mount_removed), nullptr);
|
||||
}
|
||||
}
|
||||
hash_uses++;
|
||||
G_UNLOCK(hash);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Folder::onIdleReload() {
|
||||
/* check if folder still exists */
|
||||
reload();
|
||||
// G_LOCK(query);
|
||||
has_idle_reload_handler = false;
|
||||
// G_UNLOCK(query);
|
||||
}
|
||||
|
||||
void Folder::queueReload() {
|
||||
// G_LOCK(query);
|
||||
if(!has_idle_reload_handler) {
|
||||
has_idle_reload_handler = true;
|
||||
QTimer::singleShot(0, this, &Folder::onIdleReload);
|
||||
}
|
||||
// G_UNLOCK(query);
|
||||
}
|
||||
|
||||
void Folder::onFileInfoFinished() {
|
||||
FileInfoJob* job = static_cast<FileInfoJob*>(sender());
|
||||
fileinfoJobs_.erase(std::find(fileinfoJobs_.cbegin(), fileinfoJobs_.cend(), job));
|
||||
|
||||
if(job->isCancelled())
|
||||
return;
|
||||
|
||||
FileInfoList files_to_add;
|
||||
std::vector<FileInfoPair> files_to_update;
|
||||
|
||||
const auto& paths = job->paths();
|
||||
const auto& infos = job->files();
|
||||
auto path_it = paths.cbegin();
|
||||
auto info_it = infos.cbegin();
|
||||
for(; path_it != paths.cend() && info_it != infos.cend(); ++path_it, ++info_it) {
|
||||
const auto& path = *path_it;
|
||||
const auto& info = *info_it;
|
||||
|
||||
if(path == dirPath_) { // got the info for the folder itself.
|
||||
dirInfo_ = info;
|
||||
}
|
||||
else {
|
||||
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));
|
||||
}
|
||||
else { // newly added
|
||||
files_to_add.push_back(info);
|
||||
}
|
||||
files_[info->name()] = info;
|
||||
}
|
||||
}
|
||||
if(!files_to_add.empty()) {
|
||||
Q_EMIT filesAdded(files_to_add);
|
||||
}
|
||||
if(!files_to_update.empty()) {
|
||||
Q_EMIT filesChanged(files_to_update);
|
||||
}
|
||||
Q_EMIT contentChanged();
|
||||
}
|
||||
|
||||
void Folder::processPendingChanges() {
|
||||
has_idle_update_handler = false;
|
||||
// FmFileInfoJob* job = nullptr;
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
|
||||
// idle_handler = 0;
|
||||
/* if we were asked to block updates let delay it for now */
|
||||
if(stop_emission) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileInfoJob* info_job = nullptr;
|
||||
if(!paths_to_update.empty() || !paths_to_add.empty()) {
|
||||
FilePathList paths;
|
||||
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};
|
||||
paths_to_update.clear();
|
||||
paths_to_add.clear();
|
||||
}
|
||||
|
||||
if(info_job) {
|
||||
fileinfoJobs_.push_back(info_job);
|
||||
info_job->setAutoDelete(true);
|
||||
connect(info_job, &FileInfoJob::finished, this, &Folder::onFileInfoFinished, Qt::BlockingQueuedConnection);
|
||||
info_job->runAsync();
|
||||
#if 0
|
||||
pending_jobs = g_slist_prepend(pending_jobs, job);
|
||||
if(!fm_job_run_async(FM_JOB(job))) {
|
||||
pending_jobs = g_slist_remove(pending_jobs, job);
|
||||
g_object_unref(job);
|
||||
g_critical("failed to start folder update job");
|
||||
}
|
||||
#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 */
|
||||
queryFilesystemInfo();
|
||||
pending_change_notify = false;
|
||||
}
|
||||
|
||||
if(filesystem_info_pending) {
|
||||
Q_EMIT fileSystemChanged();
|
||||
filesystem_info_pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* should be called only with G_LOCK(lists) on! */
|
||||
void Folder::queueUpdate() {
|
||||
// qDebug() << "queue_update:" << !has_idle_handler << paths_to_add.size() << paths_to_update.size() << paths_to_del.size();
|
||||
if(!has_idle_update_handler) {
|
||||
QTimer::singleShot(0, this, &Folder::processPendingChanges);
|
||||
has_idle_update_handler = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* returns true if reference was taken from path */
|
||||
bool Folder::eventFileAdded(const FilePath &path) {
|
||||
bool added = true;
|
||||
// G_LOCK(lists);
|
||||
/* make sure that the file is not already queued for addition. */
|
||||
if(std::find(paths_to_add.cbegin(), paths_to_add.cend(), path) == paths_to_add.cend()) {
|
||||
if(files_.find(path.baseName().get()) != files_.end()) { // the file already exists, update instead
|
||||
if(std::find(paths_to_update.cbegin(), paths_to_update.cend(), path) == paths_to_update.cend()) {
|
||||
paths_to_update.push_back(path);
|
||||
}
|
||||
}
|
||||
else { // newly added file
|
||||
paths_to_add.push_back(path);
|
||||
}
|
||||
/* bug #3591771: 'ln -fns . test' leave no file visible in folder.
|
||||
If it is queued for deletion then cancel that operation */
|
||||
paths_to_del.erase(std::remove(paths_to_del.begin(), paths_to_del.end(), path), paths_to_del.cend());
|
||||
}
|
||||
else
|
||||
/* file already queued for adding, don't duplicate */
|
||||
{
|
||||
added = false;
|
||||
}
|
||||
if(added) {
|
||||
queueUpdate();
|
||||
}
|
||||
// G_UNLOCK(lists);
|
||||
return added;
|
||||
}
|
||||
|
||||
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. */
|
||||
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()) {
|
||||
/* 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. */
|
||||
paths_to_update.push_back(path);
|
||||
added = true;
|
||||
queueUpdate();
|
||||
}
|
||||
else {
|
||||
added = false;
|
||||
}
|
||||
// G_UNLOCK(lists);
|
||||
return added;
|
||||
}
|
||||
|
||||
void Folder::eventFileDeleted(const FilePath& path) {
|
||||
// qDebug() << "delete " << path.baseName().get();
|
||||
// G_LOCK(lists);
|
||||
if(files_.find(path.baseName().get()) != files_.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());
|
||||
paths_to_update.erase(std::remove(paths_to_update.begin(), paths_to_update.end(), path), paths_to_update.cend());
|
||||
queueUpdate();
|
||||
// G_UNLOCK(lists);
|
||||
}
|
||||
|
||||
|
||||
void Folder::onDirChanged(GFileMonitorEvent evt) {
|
||||
switch(evt) {
|
||||
case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
|
||||
/* g_debug("folder is going to be unmounted"); */
|
||||
break;
|
||||
case G_FILE_MONITOR_EVENT_UNMOUNTED:
|
||||
Q_EMIT unmount();
|
||||
/* g_debug("folder is unmounted"); */
|
||||
queueReload();
|
||||
break;
|
||||
case G_FILE_MONITOR_EVENT_DELETED:
|
||||
Q_EMIT removed();
|
||||
/* g_debug("folder is deleted"); */
|
||||
break;
|
||||
case G_FILE_MONITOR_EVENT_CREATED:
|
||||
queueReload();
|
||||
break;
|
||||
case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
|
||||
case G_FILE_MONITOR_EVENT_CHANGED: {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
pending_change_notify = true;
|
||||
if(std::find(paths_to_update.cbegin(), paths_to_update.cend(), dirPath_) != paths_to_update.cend()) {
|
||||
paths_to_update.push_back(dirPath_);
|
||||
queueUpdate();
|
||||
}
|
||||
/* g_debug("folder is changed"); */
|
||||
break;
|
||||
}
|
||||
#if GLIB_CHECK_VERSION(2,24,0)
|
||||
case G_FILE_MONITOR_EVENT_MOVED:
|
||||
#endif
|
||||
case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
|
||||
;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Folder::onFileChangeEvents(GFileMonitor* /*monitor*/, GFile* gf, GFile* /*other_file*/, GFileMonitorEvent evt) {
|
||||
/* const char* names[]={
|
||||
"G_FILE_MONITOR_EVENT_CHANGED",
|
||||
"G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT",
|
||||
"G_FILE_MONITOR_EVENT_DELETED",
|
||||
"G_FILE_MONITOR_EVENT_CREATED",
|
||||
"G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED",
|
||||
"G_FILE_MONITOR_EVENT_PRE_UNMOUNT",
|
||||
"G_FILE_MONITOR_EVENT_UNMOUNTED"
|
||||
}; */
|
||||
if(dirPath_ == gf) {
|
||||
onDirChanged(evt);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
auto path = FilePath{gf, true};
|
||||
/* NOTE: sometimes, for unknown reasons, GFileMonitor gives us the
|
||||
* same event of the same file for multiple times. So we need to
|
||||
* check for duplications ourselves here. */
|
||||
switch(evt) {
|
||||
case G_FILE_MONITOR_EVENT_CREATED:
|
||||
eventFileAdded(path);
|
||||
break;
|
||||
case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
|
||||
case G_FILE_MONITOR_EVENT_CHANGED:
|
||||
eventFileChanged(path);
|
||||
break;
|
||||
case G_FILE_MONITOR_EVENT_DELETED:
|
||||
eventFileDeleted(path);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
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();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Folder::hasCutFiles() {
|
||||
return cutFilesHashSet_
|
||||
&& !cutFilesHashSet_->empty()
|
||||
&& cutFilesDirPath_ == dirPath_;
|
||||
}
|
||||
|
||||
void Folder::setCutFiles(const std::shared_ptr<const HashSet>& cutFilesHashSet) {
|
||||
if(cutFilesHashSet_ && !cutFilesHashSet_->empty()) {
|
||||
lastCutFilesDirPath_ = cutFilesDirPath_;
|
||||
}
|
||||
cutFilesDirPath_ = dirPath_;
|
||||
cutFilesHashSet_ = cutFilesHashSet;
|
||||
}
|
||||
|
||||
void Folder::onDirListFinished() {
|
||||
DirListJob* job = static_cast<DirListJob*>(sender());
|
||||
if(job->isCancelled()) { // this is a cancelled job, ignore!
|
||||
if(job == dirlist_job) {
|
||||
dirlist_job = nullptr;
|
||||
}
|
||||
Q_EMIT finishLoading();
|
||||
return;
|
||||
}
|
||||
dirInfo_ = job->dirInfo();
|
||||
|
||||
FileInfoList files_to_add;
|
||||
std::vector<FileInfoPair> files_to_update;
|
||||
const auto& infos = job->files();
|
||||
|
||||
// with "search://", there is no update for infos and all of them should be added
|
||||
if(strcmp(dirPath_.uriScheme().get(), "search") == 0) {
|
||||
files_to_add = infos;
|
||||
for(auto& file: files_to_add) {
|
||||
files_[file->name()] = file;
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto info_it = infos.cbegin();
|
||||
for(; info_it != infos.cend(); ++info_it) {
|
||||
const auto& info = *info_it;
|
||||
auto it = files_.find(info->name());
|
||||
if(it != files_.end()) {
|
||||
files_to_update.push_back(std::make_pair(it->second, info));
|
||||
}
|
||||
else {
|
||||
files_to_add.push_back(info);
|
||||
}
|
||||
files_[info->name()] = info;
|
||||
}
|
||||
}
|
||||
|
||||
if(!files_to_add.empty()) {
|
||||
Q_EMIT filesAdded(files_to_add);
|
||||
}
|
||||
if(!files_to_update.empty()) {
|
||||
Q_EMIT filesChanged(files_to_update);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if(dirlist_job->isCancelled() && !wants_incremental) {
|
||||
GList* l;
|
||||
for(l = fm_file_info_list_peek_head_link(job->files); l; l = l->next) {
|
||||
FmFileInfo* inf = (FmFileInfo*)l->data;
|
||||
files = g_slist_prepend(files, inf);
|
||||
fm_file_info_list_push_tail(files, inf);
|
||||
}
|
||||
if(G_LIKELY(files)) {
|
||||
GSList* l;
|
||||
|
||||
G_LOCK(lists);
|
||||
if(defer_content_test && fm_path_is_native(dir_path))
|
||||
/* we got only basic info on content, schedule update it now */
|
||||
for(l = files; l; l = l->next)
|
||||
files_to_update = g_slist_prepend(files_to_update,
|
||||
fm_path_ref(fm_file_info_get_path(l->data)));
|
||||
G_UNLOCK(lists);
|
||||
g_signal_emit(folder, signals[FILES_ADDED], 0, files);
|
||||
g_slist_free(files);
|
||||
}
|
||||
|
||||
if(job->dir_fi) {
|
||||
dir_fi = fm_file_info_ref(job->dir_fi);
|
||||
}
|
||||
|
||||
/* Some new files are created while FmDirListJob is loading the folder. */
|
||||
G_LOCK(lists);
|
||||
if(G_UNLIKELY(files_to_add)) {
|
||||
/* This should be a very rare case. Could this happen? */
|
||||
GSList* l;
|
||||
for(l = files_to_add; l;) {
|
||||
FmPath* path = l->data;
|
||||
GSList* next = l->next;
|
||||
if(_Folder::get_file_by_path(folder, path)) {
|
||||
/* we already have the file. remove it from files_to_add,
|
||||
* and put it in files_to_update instead.
|
||||
* No strdup for name is needed here. We steal
|
||||
* the string from files_to_add.*/
|
||||
files_to_update = g_slist_prepend(files_to_update, path);
|
||||
files_to_add = g_slist_delete_link(files_to_add, l);
|
||||
}
|
||||
l = next;
|
||||
}
|
||||
}
|
||||
G_UNLOCK(lists);
|
||||
}
|
||||
else if(!dir_fi && job->dir_fi)
|
||||
/* we may need dir_fi for incremental folders too */
|
||||
{
|
||||
dir_fi = fm_file_info_ref(job->dir_fi);
|
||||
}
|
||||
g_object_unref(dirlist_job);
|
||||
#endif
|
||||
|
||||
dirlist_job = nullptr;
|
||||
Q_EMIT finishLoading();
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
|
||||
void on_dirlist_job_files_found(FmDirListJob* job, GSList* files, gpointer user_data) {
|
||||
FmFolder* folder = FM_FOLDER(user_data);
|
||||
GSList* l;
|
||||
for(l = files; l; l = l->next) {
|
||||
FmFileInfo* file = FM_FILE_INFO(l->data);
|
||||
fm_file_info_list_push_tail(files, file);
|
||||
}
|
||||
if(G_UNLIKELY(!dir_fi && job->dir_fi))
|
||||
/* we may want info while folder is still loading */
|
||||
{
|
||||
dir_fi = fm_file_info_ref(job->dir_fi);
|
||||
}
|
||||
g_signal_emit(folder, signals[FILES_ADDED], 0, files);
|
||||
}
|
||||
|
||||
ErrorAction on_dirlist_job_error(FmDirListJob* job, GError* err, FmJobErrorSeverity severity, FmFolder* folder) {
|
||||
guint ret;
|
||||
/* it's possible that some signal handlers tries to free the folder
|
||||
* when errors occurs, so let's g_object_ref here. */
|
||||
g_object_ref(folder);
|
||||
g_signal_emit(folder, signals[ERROR], 0, err, (guint)severity, &ret);
|
||||
g_object_unref(folder);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void free_dirlist_job(FmFolder* folder) {
|
||||
if(wants_incremental) {
|
||||
g_signal_handlers_disconnect_by_func(dirlist_job, on_dirlist_job_files_found, folder);
|
||||
}
|
||||
g_signal_handlers_disconnect_by_func(dirlist_job, on_dirlist_job_finished, folder);
|
||||
g_signal_handlers_disconnect_by_func(dirlist_job, on_dirlist_job_error, folder);
|
||||
fm_job_cancel(FM_JOB(dirlist_job));
|
||||
g_object_unref(dirlist_job);
|
||||
dirlist_job = nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void Folder::reload() {
|
||||
// cancel in-progress jobs if there are any
|
||||
GError* err = nullptr;
|
||||
// cancel directory monitoring
|
||||
if(dirMonitor_) {
|
||||
g_signal_handlers_disconnect_by_data(dirMonitor_.get(), this);
|
||||
dirMonitor_.reset();
|
||||
}
|
||||
|
||||
/* clear all update-lists now, see SF bug #919 - if update comes before
|
||||
listing job is finished, a duplicate may be created in the folder */
|
||||
if(has_idle_update_handler) {
|
||||
// FIXME: cancel the idle handler
|
||||
paths_to_add.clear();
|
||||
paths_to_update.clear();
|
||||
paths_to_del.clear();
|
||||
|
||||
// cancel any file info job in progress.
|
||||
for(auto job: fileinfoJobs_) {
|
||||
job->cancel();
|
||||
disconnect(job, &FileInfoJob::finished, this, &Folder::onFileInfoFinished);
|
||||
}
|
||||
fileinfoJobs_.clear();
|
||||
}
|
||||
|
||||
/* remove all existing files */
|
||||
if(!files_.empty()) {
|
||||
// FIXME: this is not very efficient :(
|
||||
auto tmp = files();
|
||||
files_.clear();
|
||||
Q_EMIT filesRemoved(tmp);
|
||||
}
|
||||
|
||||
/* Tell the world that we're about to reload the folder.
|
||||
* It might be a good idea for users of the folder to disconnect
|
||||
* from the folder temporarily and reconnect to it again after
|
||||
* the folder complete the loading. This might reduce some
|
||||
* unnecessary signal handling and UI updates. */
|
||||
Q_EMIT startLoading();
|
||||
|
||||
dirInfo_.reset(); // clear dir info
|
||||
|
||||
/* also re-create a new file monitor */
|
||||
// mon = GFileMonitorPtr{fm_monitor_directory(dir_path.gfile().get(), &err), false};
|
||||
// FIXME: should we make this cancellable?
|
||||
dirMonitor_ = GFileMonitorPtr{
|
||||
g_file_monitor_directory(dirPath_.gfile().get(), G_FILE_MONITOR_WATCH_MOUNTS, nullptr, &err),
|
||||
false
|
||||
};
|
||||
|
||||
if(dirMonitor_) {
|
||||
g_signal_connect(dirMonitor_.get(), "changed", G_CALLBACK(_onFileChangeEvents), this);
|
||||
}
|
||||
else {
|
||||
qDebug("file monitor cannot be created: %s", err->message);
|
||||
g_error_free(err);
|
||||
}
|
||||
|
||||
Q_EMIT contentChanged();
|
||||
|
||||
/* run a new dir listing job */
|
||||
// FIXME:
|
||||
// defer_content_test = fm_config->defer_content_test;
|
||||
dirlist_job = new DirListJob(dirPath_, defer_content_test ? DirListJob::FAST : DirListJob::DETAILED,
|
||||
hasCutFiles() ? cutFilesHashSet_ : nullptr);
|
||||
dirlist_job->setAutoDelete(true);
|
||||
connect(dirlist_job, &DirListJob::error, this, &Folder::error, Qt::BlockingQueuedConnection);
|
||||
connect(dirlist_job, &DirListJob::finished, this, &Folder::onDirListFinished, Qt::BlockingQueuedConnection);
|
||||
|
||||
#if 0
|
||||
if(wants_incremental) {
|
||||
g_signal_connect(dirlist_job, "files-found", G_CALLBACK(on_dirlist_job_files_found), folder);
|
||||
}
|
||||
fm_dir_list_job_set_incremental(dirlist_job, wants_incremental);
|
||||
#endif
|
||||
|
||||
dirlist_job->runAsync();
|
||||
|
||||
/* also reload filesystem info.
|
||||
* FIXME: is this needed? */
|
||||
queryFilesystemInfo();
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
/**
|
||||
* Folder::is_incremental
|
||||
* @folder: folder to test
|
||||
*
|
||||
* Checks if a folder is incrementally loaded.
|
||||
* After an FmFolder object is obtained from calling Folder::from_path(),
|
||||
* if it's not yet loaded, it begins loading the content of the folder
|
||||
* and emits "start-loading" signal. Most of the time, the info of the
|
||||
* files in the folder becomes available only after the folder is fully
|
||||
* loaded. That means, after the "finish-loading" signal is emitted.
|
||||
* Before the loading is finished, Folder::get_files() returns nothing.
|
||||
* You can tell if a folder is still being loaded with Folder::is_loaded().
|
||||
*
|
||||
* However, for some special FmFolder types, such as the ones handling
|
||||
* search:// URIs, we want to access the file infos while the folder is
|
||||
* still being loaded (the search is still ongoing).
|
||||
* The content of the folder grows incrementally and Folder::get_files()
|
||||
* returns files currently being loaded even when the folder is not
|
||||
* fully loaded. This is what we called incremental.
|
||||
* Folder::is_incremental() tells you if the FmFolder has this feature.
|
||||
*
|
||||
* Returns: %true if @folder is incrementally loaded
|
||||
*
|
||||
* Since: 1.0.2
|
||||
*/
|
||||
bool Folder::is_incremental(FmFolder* folder) {
|
||||
return wants_incremental;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool Folder::getFilesystemInfo(uint64_t* total_size, uint64_t* free_size) const {
|
||||
if(has_fs_info) {
|
||||
*total_size = fs_total_size;
|
||||
*free_size = fs_free_size;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void Folder::onFileSystemInfoFinished() {
|
||||
FileSystemInfoJob* job = static_cast<FileSystemInfoJob*>(sender());
|
||||
if(job->isCancelled() || job != fsInfoJob_) { // this is a cancelled job, ignore!
|
||||
fsInfoJob_ = nullptr;
|
||||
has_fs_info = false;
|
||||
return;
|
||||
}
|
||||
has_fs_info = job->isAvailable();
|
||||
fs_total_size = job->size();
|
||||
fs_free_size = job->freeSize();
|
||||
filesystem_info_pending = true;
|
||||
fsInfoJob_ = nullptr;
|
||||
queueUpdate();
|
||||
}
|
||||
|
||||
|
||||
void Folder::queryFilesystemInfo() {
|
||||
// G_LOCK(query);
|
||||
if(fsInfoJob_)
|
||||
return;
|
||||
fsInfoJob_ = new FileSystemInfoJob{dirPath_};
|
||||
fsInfoJob_->setAutoDelete(true);
|
||||
connect(fsInfoJob_, &FileSystemInfoJob::finished, this, &Folder::onFileSystemInfoFinished, Qt::BlockingQueuedConnection);
|
||||
|
||||
fsInfoJob_->runAsync();
|
||||
// G_UNLOCK(query);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Folder::block_updates
|
||||
* @folder: folder to apply
|
||||
*
|
||||
* Blocks emitting signals for changes in folder, i.e. if some file was
|
||||
* added, changed, or removed in folder after this API, no signal will be
|
||||
* sent until next call to Folder::unblock_updates().
|
||||
*
|
||||
* Since: 1.2.0
|
||||
*/
|
||||
void Folder::block_updates(FmFolder* folder) {
|
||||
/* g_debug("Folder::block_updates %p", folder); */
|
||||
G_LOCK(lists);
|
||||
/* just set the flag */
|
||||
stop_emission = true;
|
||||
G_UNLOCK(lists);
|
||||
}
|
||||
|
||||
/**
|
||||
* Folder::unblock_updates
|
||||
* @folder: folder to apply
|
||||
*
|
||||
* Unblocks emitting signals for changes in folder. If some changes were
|
||||
* in folder after previous call to Folder::block_updates() then these
|
||||
* changes will be sent after this call.
|
||||
*
|
||||
* Since: 1.2.0
|
||||
*/
|
||||
void Folder::unblock_updates(FmFolder* folder) {
|
||||
/* g_debug("Folder::unblock_updates %p", folder); */
|
||||
G_LOCK(lists);
|
||||
stop_emission = false;
|
||||
/* query update now */
|
||||
queue_update(folder);
|
||||
G_UNLOCK(lists);
|
||||
/* g_debug("Folder::unblock_updates OK"); */
|
||||
}
|
||||
|
||||
/**
|
||||
* Folder::make_directory
|
||||
* @folder: folder to apply
|
||||
* @name: display name for new directory
|
||||
* @error: (allow-none) (out): location to save error
|
||||
*
|
||||
* Creates new directory in given @folder.
|
||||
*
|
||||
* Returns: %true in case of success.
|
||||
*
|
||||
* Since: 1.2.0
|
||||
*/
|
||||
bool Folder::make_directory(FmFolder* folder, const char* name, GError** error) {
|
||||
GFile* dir, *gf;
|
||||
FmPath* path;
|
||||
bool ok;
|
||||
|
||||
dir = fm_path_to_gfile(dir_path);
|
||||
gf = g_file_get_child_for_display_name(dir, name, error);
|
||||
g_object_unref(dir);
|
||||
if(gf == nullptr) {
|
||||
return false;
|
||||
}
|
||||
ok = g_file_make_directory(gf, nullptr, error);
|
||||
if(ok) {
|
||||
path = fm_path_new_for_gfile(gf);
|
||||
if(!_Folder::event_file_added(folder, path)) {
|
||||
fm_path_unref(path);
|
||||
}
|
||||
}
|
||||
g_object_unref(gf);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void Folder::content_changed(FmFolder* folder) {
|
||||
if(has_fs_info && !fs_info_not_avail) {
|
||||
Folder::query_filesystem_info(folder);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* NOTE:
|
||||
* GFileMonitor has some significant limitations:
|
||||
* 1. Currently it can correctly emit unmounted event for a directory.
|
||||
* 2. After a directory is unmounted, its content changes.
|
||||
* Inotify does not fire events for this so a forced reload is needed.
|
||||
* 3. If a folder is empty, and later a filesystem is mounted to the
|
||||
* folder, its content should reflect the content of the newly mounted
|
||||
* filesystem. However, GFileMonitor and inotify do not emit events
|
||||
* for this case. A forced reload might be needed for this case as well.
|
||||
* 4. Some limitations come from Linux/inotify. If FAM/gamin is used,
|
||||
* the condition may be different. More testing is needed.
|
||||
*/
|
||||
void Folder::onMountAdded(const Mount& mnt) {
|
||||
/* If a filesystem is mounted over an existing folder,
|
||||
* we need to refresh the content of the folder to reflect
|
||||
* the changes. Besides, we need to create a new GFileMonitor
|
||||
* for the newly-mounted filesystem as the inode already changed.
|
||||
* GFileMonitor cannot detect this kind of changes caused by mounting.
|
||||
* So let's do it ourselves. */
|
||||
auto mountRoot = mnt.root();
|
||||
if(mountRoot.isPrefixOf(dirPath_)) {
|
||||
queueReload();
|
||||
}
|
||||
/* g_debug("FmFolder::mount_added"); */
|
||||
}
|
||||
|
||||
void Folder::onMountRemoved(const Mount& mnt) {
|
||||
/* g_debug("FmFolder::mount_removed"); */
|
||||
|
||||
/* NOTE: gvfs does not emit unmount signals for remote folders since
|
||||
* GFileMonitor does not support remote filesystems at all.
|
||||
* So here is the side effect, no unmount notifications.
|
||||
* We need to generate the signal ourselves. */
|
||||
if(!dirMonitor_) {
|
||||
// this is only needed when we don't have a GFileMonitor
|
||||
auto mountRoot = mnt.root();
|
||||
if(mountRoot.isPrefixOf(dirPath_)) {
|
||||
// if the current folder is under the unmounted path, generate the event ourselves
|
||||
onDirChanged(G_FILE_MONITOR_EVENT_UNMOUNTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Fm
|
196
src/core/folder.h
Normal file
196
src/core/folder.h
Normal file
@ -0,0 +1,196 @@
|
||||
/*
|
||||
* 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 __LIBFM2_QT_FM_FOLDER_H__
|
||||
#define __LIBFM2_QT_FM_FOLDER_H__
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
|
||||
#include <QObject>
|
||||
#include <QtGlobal>
|
||||
#include "../libfmqtglobals.h"
|
||||
|
||||
#include "gioptrs.h"
|
||||
#include "fileinfo.h"
|
||||
#include "job.h"
|
||||
#include "volumemanager.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class DirListJob;
|
||||
class FileSystemInfoJob;
|
||||
class FileInfoJob;
|
||||
|
||||
|
||||
class LIBFM_QT_API Folder: public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
explicit Folder();
|
||||
|
||||
explicit Folder(const FilePath& path);
|
||||
|
||||
virtual ~Folder();
|
||||
|
||||
static std::shared_ptr<Folder> fromPath(const FilePath& path);
|
||||
|
||||
bool makeDirectory(const char* name, GError** error);
|
||||
|
||||
void queryFilesystemInfo();
|
||||
|
||||
bool getFilesystemInfo(uint64_t* total_size, uint64_t* free_size) const;
|
||||
|
||||
void reload();
|
||||
|
||||
bool isIncremental() const;
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
bool isLoaded() const;
|
||||
|
||||
std::shared_ptr<const FileInfo> fileByName(const char* name) const;
|
||||
|
||||
bool isEmpty() const;
|
||||
|
||||
FileInfoList files() const;
|
||||
|
||||
const FilePath& path() const;
|
||||
|
||||
const std::shared_ptr<const FileInfo> &info() const;
|
||||
|
||||
bool hadCutFilesUnset();
|
||||
|
||||
bool hasCutFiles();
|
||||
|
||||
void setCutFiles(const std::shared_ptr<const HashSet>& cutFilesHashSet);
|
||||
|
||||
void forEachFile(std::function<void (const std::shared_ptr<const FileInfo>&)> func) const {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
for(auto it = files_.begin(); it != files_.end(); ++it) {
|
||||
func(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void startLoading();
|
||||
|
||||
void finishLoading();
|
||||
|
||||
void filesAdded(FileInfoList& addedFiles);
|
||||
|
||||
void filesChanged(std::vector<FileInfoPair>& changePairs);
|
||||
|
||||
void filesRemoved(FileInfoList& removedFiles);
|
||||
|
||||
void removed();
|
||||
|
||||
void changed();
|
||||
|
||||
void unmount();
|
||||
|
||||
void contentChanged();
|
||||
|
||||
void fileSystemChanged();
|
||||
|
||||
// FIXME: this API design is bad. We leave this here to be compatible with the old libfm C API.
|
||||
// It might be better to remember the error state while loading the folder, and let the user of the
|
||||
// API handle the error on finish.
|
||||
void error(const GErrorPtr& err, Job::ErrorSeverity severity, Job::ErrorAction& response);
|
||||
|
||||
private:
|
||||
|
||||
static void _onFileChangeEvents(GFileMonitor* monitor, GFile* file, GFile* other_file, GFileMonitorEvent event_type, Folder* _this) {
|
||||
_this->onFileChangeEvents(monitor, file, other_file, event_type);
|
||||
}
|
||||
void onFileChangeEvents(GFileMonitor* monitor, GFile* file, GFile* other_file, GFileMonitorEvent event_type);
|
||||
void onDirChanged(GFileMonitorEvent event_type);
|
||||
|
||||
void queueUpdate();
|
||||
void queueReload();
|
||||
|
||||
bool eventFileAdded(const FilePath &path);
|
||||
bool eventFileChanged(const FilePath &path);
|
||||
void eventFileDeleted(const FilePath &path);
|
||||
|
||||
private Q_SLOTS:
|
||||
|
||||
void processPendingChanges();
|
||||
|
||||
void onDirListFinished();
|
||||
|
||||
void onFileSystemInfoFinished();
|
||||
|
||||
void onFileInfoFinished();
|
||||
|
||||
void onIdleReload();
|
||||
|
||||
void onMountAdded(const Mount& mnt);
|
||||
|
||||
void onMountRemoved(const Mount& mnt);
|
||||
|
||||
private:
|
||||
FilePath dirPath_;
|
||||
GFileMonitorPtr dirMonitor_;
|
||||
|
||||
std::shared_ptr<const FileInfo> dirInfo_;
|
||||
DirListJob* dirlist_job;
|
||||
std::vector<FileInfoJob*> fileinfoJobs_;
|
||||
FileSystemInfoJob* fsInfoJob_;
|
||||
|
||||
std::shared_ptr<VolumeManager> volumeManager_;
|
||||
|
||||
/* for file monitor */
|
||||
bool has_idle_reload_handler;
|
||||
bool has_idle_update_handler;
|
||||
std::vector<FilePath> paths_to_add;
|
||||
std::vector<FilePath> paths_to_update;
|
||||
std::vector<FilePath> paths_to_del;
|
||||
// GSList* pending_jobs;
|
||||
bool pending_change_notify;
|
||||
bool filesystem_info_pending;
|
||||
|
||||
bool wants_incremental;
|
||||
bool stop_emission; /* don't set it 1 bit to not lock other bits */
|
||||
|
||||
std::unordered_map<const std::string, std::shared_ptr<const FileInfo>, std::hash<std::string>> files_;
|
||||
|
||||
/* filesystem info - set in query thread, read in main */
|
||||
uint64_t fs_total_size;
|
||||
uint64_t fs_free_size;
|
||||
GCancellablePtr fs_size_cancellable;
|
||||
|
||||
bool has_fs_info : 1;
|
||||
bool defer_content_test : 1;
|
||||
|
||||
static std::unordered_map<FilePath, std::weak_ptr<Folder>, FilePathHash> cache_;
|
||||
static FilePath cutFilesDirPath_;
|
||||
static FilePath lastCutFilesDirPath_;
|
||||
static std::shared_ptr<const HashSet> cutFilesHashSet_;
|
||||
static std::mutex mutex_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __LIBFM_QT_FM2_FOLDER_H__
|
137
src/core/gioptrs.h
Normal file
137
src/core/gioptrs.h
Normal file
@ -0,0 +1,137 @@
|
||||
#ifndef GIOPTRS_H
|
||||
#define GIOPTRS_H
|
||||
// define smart pointers for GIO data types
|
||||
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
#include "gobjectptr.h"
|
||||
#include "cstrptr.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
typedef GObjectPtr<GFile> GFilePtr;
|
||||
typedef GObjectPtr<GFileInfo> GFileInfoPtr;
|
||||
typedef GObjectPtr<GFileMonitor> GFileMonitorPtr;
|
||||
typedef GObjectPtr<GCancellable> GCancellablePtr;
|
||||
typedef GObjectPtr<GFileEnumerator> GFileEnumeratorPtr;
|
||||
|
||||
typedef GObjectPtr<GInputStream> GInputStreamPtr;
|
||||
typedef GObjectPtr<GFileInputStream> GFileInputStreamPtr;
|
||||
typedef GObjectPtr<GOutputStream> GOutputStreamPtr;
|
||||
typedef GObjectPtr<GFileOutputStream> GFileOutputStreamPtr;
|
||||
|
||||
typedef GObjectPtr<GIcon> GIconPtr;
|
||||
|
||||
typedef GObjectPtr<GVolumeMonitor> GVolumeMonitorPtr;
|
||||
typedef GObjectPtr<GVolume> GVolumePtr;
|
||||
typedef GObjectPtr<GMount> GMountPtr;
|
||||
|
||||
typedef GObjectPtr<GAppInfo> GAppInfoPtr;
|
||||
|
||||
|
||||
class GErrorPtr {
|
||||
public:
|
||||
GErrorPtr(): err_{nullptr} {
|
||||
}
|
||||
|
||||
GErrorPtr(GError*&& err) noexcept: err_{err} {
|
||||
err = nullptr;
|
||||
}
|
||||
|
||||
GErrorPtr(const GErrorPtr& other) = delete;
|
||||
|
||||
GErrorPtr(GErrorPtr&& other) noexcept: err_{other.err_} {
|
||||
other.err_ = nullptr;
|
||||
}
|
||||
|
||||
GErrorPtr(std::uint32_t domain, unsigned int code, const char* msg):
|
||||
GErrorPtr{g_error_new_literal(domain, code, msg)} {
|
||||
}
|
||||
|
||||
GErrorPtr(std::uint32_t domain, unsigned int code, const QString& msg):
|
||||
GErrorPtr{domain, code, msg.toUtf8().constData()} {
|
||||
}
|
||||
|
||||
~GErrorPtr() {
|
||||
reset();
|
||||
}
|
||||
|
||||
std::uint32_t domain() const {
|
||||
if(err_ != nullptr) {
|
||||
return err_->domain;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int code() const {
|
||||
if(err_ != nullptr) {
|
||||
return err_->code;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString message() const {
|
||||
if(err_ != nullptr) {
|
||||
return err_->message;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if(err_) {
|
||||
g_error_free(err_);
|
||||
}
|
||||
err_ = nullptr;
|
||||
}
|
||||
|
||||
GError* get() const {
|
||||
return err_;
|
||||
}
|
||||
|
||||
GErrorPtr& operator = (const GErrorPtr& other) = delete;
|
||||
|
||||
GErrorPtr& operator = (GErrorPtr&& other) noexcept {
|
||||
reset();
|
||||
err_ = other.err_;
|
||||
other.err_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GErrorPtr& operator = (GError*&& err) {
|
||||
reset();
|
||||
err_ = err;
|
||||
err_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GError** operator&() {
|
||||
return &err_;
|
||||
}
|
||||
|
||||
GError* operator->() {
|
||||
return err_;
|
||||
}
|
||||
|
||||
bool operator == (const GErrorPtr& other) const {
|
||||
return err_ == other.err_;
|
||||
}
|
||||
|
||||
bool operator == (GError* err) const {
|
||||
return err_ == err;
|
||||
}
|
||||
|
||||
bool operator != (std::nullptr_t) const {
|
||||
return err_ != nullptr;
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return err_ != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
GError* err_;
|
||||
};
|
||||
|
||||
} //namespace Fm
|
||||
|
||||
#endif // GIOPTRS_H
|
104
src/core/gobjectptr.h
Normal file
104
src/core/gobjectptr.h
Normal file
@ -0,0 +1,104 @@
|
||||
#ifndef FM2_GOBJECTPTR_H
|
||||
#define FM2_GOBJECTPTR_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
#include <cstddef>
|
||||
#include <QDebug>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
template <typename T>
|
||||
class LIBFM_QT_API GObjectPtr {
|
||||
public:
|
||||
|
||||
explicit GObjectPtr(): gobj_{nullptr} {
|
||||
}
|
||||
|
||||
explicit GObjectPtr(T* gobj, bool add_ref = true): gobj_{gobj} {
|
||||
if(gobj_ != nullptr && add_ref)
|
||||
g_object_ref(gobj_);
|
||||
}
|
||||
|
||||
GObjectPtr(const GObjectPtr& other): gobj_{other.gobj_ ? reinterpret_cast<T*>(g_object_ref(other.gobj_)) : nullptr} {
|
||||
}
|
||||
|
||||
GObjectPtr(GObjectPtr&& other) noexcept: gobj_{other.release()} {
|
||||
}
|
||||
|
||||
~GObjectPtr() {
|
||||
if(gobj_ != nullptr)
|
||||
g_object_unref(gobj_);
|
||||
}
|
||||
|
||||
T* get() const {
|
||||
return gobj_;
|
||||
}
|
||||
|
||||
T* release() {
|
||||
T* tmp = gobj_;
|
||||
gobj_ = nullptr;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if(gobj_ != nullptr)
|
||||
g_object_unref(gobj_);
|
||||
gobj_ = nullptr;
|
||||
}
|
||||
|
||||
GObjectPtr& operator = (const GObjectPtr& other) {
|
||||
if (*this == other)
|
||||
return *this;
|
||||
|
||||
if(gobj_ != nullptr)
|
||||
g_object_unref(gobj_);
|
||||
gobj_ = other.gobj_ ? reinterpret_cast<T*>(g_object_ref(other.gobj_)) : nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
GObjectPtr& operator = (GObjectPtr&& other) noexcept {
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
if(gobj_ != nullptr)
|
||||
g_object_unref(gobj_);
|
||||
gobj_ = other.release();
|
||||
return *this;
|
||||
}
|
||||
|
||||
GObjectPtr& operator = (T* gobj) {
|
||||
if (*this == gobj)
|
||||
return *this;
|
||||
|
||||
if(gobj_ != nullptr)
|
||||
g_object_unref(gobj_);
|
||||
gobj_ = gobj ? reinterpret_cast<T*>(g_object_ref(gobj_)) : nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator == (const GObjectPtr& other) const {
|
||||
return gobj_ == other.gobj_;
|
||||
}
|
||||
|
||||
bool operator == (T* gobj) const {
|
||||
return gobj_ == gobj;
|
||||
}
|
||||
|
||||
bool operator != (std::nullptr_t) const {
|
||||
return gobj_ != nullptr;
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
return gobj_ != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable T* gobj_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_GOBJECTPTR_H
|
138
src/core/iconinfo.cpp
Normal file
138
src/core/iconinfo.cpp
Normal file
@ -0,0 +1,138 @@
|
||||
#include "iconinfo.h"
|
||||
#include "iconinfo_p.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
std::unordered_map<GIcon*, std::shared_ptr<IconInfo>, IconInfo::GIconHash, IconInfo::GIconEqual> IconInfo::cache_;
|
||||
std::mutex IconInfo::mutex_;
|
||||
QIcon IconInfo::fallbackQicon_;
|
||||
|
||||
static const char* fallbackIconNames[] = {
|
||||
"unknown",
|
||||
"application-octet-stream",
|
||||
"application-x-generic",
|
||||
"text-x-generic",
|
||||
nullptr
|
||||
};
|
||||
|
||||
IconInfo::IconInfo(const char* name):
|
||||
gicon_{g_themed_icon_new(name), false} {
|
||||
}
|
||||
|
||||
IconInfo::IconInfo(const GIconPtr gicon):
|
||||
gicon_{std::move(gicon)} {
|
||||
}
|
||||
|
||||
IconInfo::~IconInfo() {
|
||||
}
|
||||
|
||||
// static
|
||||
std::shared_ptr<const IconInfo> IconInfo::fromName(const char* name) {
|
||||
GObjectPtr<GIcon> gicon{g_themed_icon_new(name), false};
|
||||
return fromGIcon(gicon);
|
||||
}
|
||||
|
||||
// static
|
||||
std::shared_ptr<const IconInfo> IconInfo::fromGIcon(GIconPtr gicon) {
|
||||
if(Q_LIKELY(gicon)) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
auto it = cache_.find(gicon.get());
|
||||
if(it != cache_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
// not found in the cache, create a new entry for it.
|
||||
auto icon = std::make_shared<IconInfo>(std::move(gicon));
|
||||
cache_.insert(std::make_pair(icon->gicon_.get(), icon));
|
||||
return icon;
|
||||
}
|
||||
return std::shared_ptr<const IconInfo>{};
|
||||
}
|
||||
|
||||
void IconInfo::updateQIcons() {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
fallbackQicon_ = QIcon();
|
||||
for(auto& elem: cache_) {
|
||||
auto& info = elem.second;
|
||||
info->internalQicon_ = QIcon();
|
||||
}
|
||||
}
|
||||
|
||||
QIcon IconInfo::qicon(const bool& transparent) const {
|
||||
if(Q_LIKELY(!transparent)) {
|
||||
if(Q_UNLIKELY(qicon_.isNull() && gicon_)) {
|
||||
if(!G_IS_FILE_ICON(gicon_.get())) {
|
||||
qicon_ = QIcon(new IconEngine{shared_from_this()});
|
||||
}
|
||||
else {
|
||||
qicon_ = internalQicon_;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // transparent == true
|
||||
if(Q_UNLIKELY(qiconTransparent_.isNull() && gicon_)) {
|
||||
if(!G_IS_FILE_ICON(gicon_.get())) {
|
||||
qiconTransparent_ = QIcon(new IconEngine{shared_from_this(), transparent});
|
||||
}
|
||||
else {
|
||||
qiconTransparent_ = internalQicon_;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !transparent ? qicon_ : qiconTransparent_;
|
||||
}
|
||||
|
||||
QIcon IconInfo::qiconFromNames(const char* const* names) {
|
||||
const gchar* const* name;
|
||||
// qDebug("names: %p", names);
|
||||
for(name = names; *name; ++name) {
|
||||
// qDebug("icon name=%s", *name);
|
||||
QIcon qicon = QIcon::fromTheme(*name);
|
||||
if(!qicon.isNull()) {
|
||||
return qicon;
|
||||
}
|
||||
}
|
||||
return QIcon();
|
||||
}
|
||||
|
||||
std::forward_list<std::shared_ptr<const IconInfo>> IconInfo::emblems() const {
|
||||
std::forward_list<std::shared_ptr<const IconInfo>> result;
|
||||
if(hasEmblems()) {
|
||||
const GList* emblems_glist = g_emblemed_icon_get_emblems(G_EMBLEMED_ICON(gicon_.get()));
|
||||
for(auto l = emblems_glist; l; l = l->next) {
|
||||
auto gemblem = G_EMBLEM(l->data);
|
||||
GIconPtr gemblem_icon{g_emblem_get_icon(gemblem), true};
|
||||
result.emplace_front(fromGIcon(gemblem_icon));
|
||||
}
|
||||
result.reverse();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QIcon IconInfo::internalQicon() const {
|
||||
if(Q_UNLIKELY(internalQicon_.isNull())) {
|
||||
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);
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
// fallback to default icon
|
||||
if(Q_UNLIKELY(internalQicon_.isNull())) {
|
||||
if(Q_UNLIKELY(fallbackQicon_.isNull())) {
|
||||
fallbackQicon_ = qiconFromNames(fallbackIconNames);
|
||||
}
|
||||
internalQicon_ = fallbackQicon_;
|
||||
}
|
||||
}
|
||||
return internalQicon_;
|
||||
}
|
||||
|
||||
} // namespace Fm
|
112
src/core/iconinfo.h
Normal file
112
src/core/iconinfo.h
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* fm-icon.h
|
||||
*
|
||||
* Copyright 2009 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
||||
* Copyright 2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
|
||||
*
|
||||
* This file is a part of the Libfm library.
|
||||
*
|
||||
* 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 __FM2_ICON_INFO_H__
|
||||
#define __FM2_ICON_INFO_H__
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include <gio/gio.h>
|
||||
#include "gioptrs.h"
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <forward_list>
|
||||
#include <QIcon>
|
||||
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API IconInfo: public std::enable_shared_from_this<IconInfo> {
|
||||
public:
|
||||
friend class IconEngine;
|
||||
|
||||
explicit IconInfo() {}
|
||||
|
||||
explicit IconInfo(const char* name);
|
||||
|
||||
explicit IconInfo(const GIconPtr gicon);
|
||||
|
||||
~IconInfo();
|
||||
|
||||
static std::shared_ptr<const IconInfo> fromName(const char* name);
|
||||
|
||||
static std::shared_ptr<const IconInfo> fromGIcon(GIconPtr gicon);
|
||||
|
||||
static std::shared_ptr<const IconInfo> fromGIcon(GIcon* gicon) {
|
||||
return fromGIcon(GIconPtr{gicon, true});
|
||||
}
|
||||
|
||||
static void updateQIcons();
|
||||
|
||||
GIconPtr gicon() const {
|
||||
return gicon_;
|
||||
}
|
||||
|
||||
QIcon qicon(const bool& transparent = false) const;
|
||||
|
||||
bool hasEmblems() const {
|
||||
return G_IS_EMBLEMED_ICON(gicon_.get());
|
||||
}
|
||||
|
||||
std::forward_list<std::shared_ptr<const IconInfo>> emblems() const;
|
||||
|
||||
bool isValid() const {
|
||||
return gicon_ != nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static QIcon qiconFromNames(const char* const* names);
|
||||
|
||||
// actual QIcon loaded by QIcon::fromTheme
|
||||
QIcon internalQicon() const;
|
||||
|
||||
struct GIconHash {
|
||||
std::size_t operator()(GIcon* gicon) const {
|
||||
return g_icon_hash(gicon);
|
||||
}
|
||||
};
|
||||
|
||||
struct GIconEqual {
|
||||
bool operator()(GIcon* gicon1, GIcon* gicon2) const {
|
||||
return g_icon_equal(gicon1, gicon2);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
GIconPtr gicon_;
|
||||
mutable QIcon qicon_;
|
||||
mutable QIcon qiconTransparent_;
|
||||
mutable QIcon internalQicon_;
|
||||
|
||||
static std::unordered_map<GIcon*, std::shared_ptr<IconInfo>, GIconHash, GIconEqual> cache_;
|
||||
static std::mutex mutex_;
|
||||
static QIcon fallbackQicon_;
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
Q_DECLARE_METATYPE(std::shared_ptr<const Fm::IconInfo>)
|
||||
|
||||
#endif /* __FM2_ICON_INFO_H__ */
|
126
src/core/iconinfo_p.h
Normal file
126
src/core/iconinfo_p.h
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* 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 FM_ICONENGINE_H
|
||||
#define FM_ICONENGINE_H
|
||||
|
||||
#include <QIconEngine>
|
||||
#include <QPainter>
|
||||
#include "../libfmqtglobals.h"
|
||||
#include "iconinfo.h"
|
||||
#include <gio/gio.h>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class IconEngine: public QIconEngine {
|
||||
public:
|
||||
|
||||
IconEngine(std::shared_ptr<const Fm::IconInfo> info, const bool& transparent = false);
|
||||
|
||||
~IconEngine();
|
||||
|
||||
virtual QSize actualSize(const QSize& size, QIcon::Mode mode, QIcon::State state) override;
|
||||
|
||||
// not supported
|
||||
virtual void addFile(const QString& /*fileName*/, const QSize& /*size*/, QIcon::Mode /*mode*/, QIcon::State /*state*/) override {}
|
||||
|
||||
// not supported
|
||||
virtual void addPixmap(const QPixmap& /*pixmap*/, QIcon::Mode /*mode*/, QIcon::State /*state*/) override {}
|
||||
|
||||
virtual QIconEngine* clone() const override;
|
||||
|
||||
virtual QString key() const override;
|
||||
|
||||
virtual void paint(QPainter* painter, const QRect& rect, QIcon::Mode mode, QIcon::State state) override;
|
||||
|
||||
virtual QPixmap pixmap(const QSize& size, QIcon::Mode mode, QIcon::State state) override;
|
||||
|
||||
virtual void virtual_hook(int id, void* data) override;
|
||||
|
||||
private:
|
||||
std::weak_ptr<const Fm::IconInfo> info_;
|
||||
bool transparent_;
|
||||
};
|
||||
|
||||
IconEngine::IconEngine(std::shared_ptr<const IconInfo> info, const bool& transparent):
|
||||
info_{info}, transparent_{transparent} {
|
||||
}
|
||||
|
||||
IconEngine::~IconEngine() {
|
||||
}
|
||||
|
||||
QSize IconEngine::actualSize(const QSize& size, QIcon::Mode mode, QIcon::State state) {
|
||||
auto info = info_.lock();
|
||||
return info ? info->internalQicon().actualSize(size, mode, state) : QSize{};
|
||||
}
|
||||
|
||||
QIconEngine* IconEngine::clone() const {
|
||||
IconEngine* engine = new IconEngine(info_.lock());
|
||||
return engine;
|
||||
}
|
||||
|
||||
QString IconEngine::key() const {
|
||||
return QStringLiteral("Fm::IconEngine");
|
||||
}
|
||||
|
||||
void IconEngine::paint(QPainter* painter, const QRect& rect, QIcon::Mode mode, QIcon::State state) {
|
||||
auto info = info_.lock();
|
||||
if(info) {
|
||||
if(transparent_) {
|
||||
painter->save();
|
||||
painter->setOpacity(0.45);
|
||||
}
|
||||
info->internalQicon().paint(painter, rect, Qt::AlignCenter, mode, state);
|
||||
if(transparent_) {
|
||||
painter->restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QPixmap IconEngine::pixmap(const QSize& size, QIcon::Mode mode, QIcon::State state) {
|
||||
auto info = info_.lock();
|
||||
return info ? info->internalQicon().pixmap(size, mode, state) : QPixmap{};
|
||||
}
|
||||
|
||||
void IconEngine::virtual_hook(int id, void* data) {
|
||||
auto info = info_.lock();
|
||||
switch(id) {
|
||||
case QIconEngine::AvailableSizesHook: {
|
||||
auto* args = reinterpret_cast<QIconEngine::AvailableSizesArgument*>(data);
|
||||
args->sizes = info ? info->internalQicon().availableSizes(args->mode, args->state) : QList<QSize>{};
|
||||
break;
|
||||
}
|
||||
case QIconEngine::IconNameHook: {
|
||||
QString* result = reinterpret_cast<QString*>(data);
|
||||
*result = info ? info->internalQicon().name() : QString{};
|
||||
break;
|
||||
}
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
|
||||
case QIconEngine::IsNullHook: {
|
||||
bool* result = reinterpret_cast<bool*>(data);
|
||||
*result = info ? info->internalQicon().isNull() : true;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM_ICONENGINE_H
|
57
src/core/job.cpp
Normal file
57
src/core/job.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
#include "job.h"
|
||||
#include "job_p.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
Job::Job():
|
||||
paused_{false},
|
||||
cancellable_{g_cancellable_new(), false},
|
||||
cancellableHandler_{g_signal_connect(cancellable_.get(), "cancelled", G_CALLBACK(_onCancellableCancelled), this)} {
|
||||
}
|
||||
|
||||
Job::~Job() {
|
||||
if(cancellable_) {
|
||||
g_cancellable_disconnect(cancellable_.get(), cancellableHandler_);
|
||||
}
|
||||
}
|
||||
|
||||
void Job::runAsync(QThread::Priority priority) {
|
||||
auto thread = new JobThread(this);
|
||||
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
|
||||
if(autoDelete()) {
|
||||
connect(this, &Job::finished, this, &Job::deleteLater);
|
||||
}
|
||||
thread->start(priority);
|
||||
}
|
||||
|
||||
void Job::cancel() {
|
||||
g_cancellable_cancel(cancellable_.get());
|
||||
}
|
||||
|
||||
void Job::run() {
|
||||
exec();
|
||||
Q_EMIT finished();
|
||||
}
|
||||
|
||||
|
||||
Job::ErrorAction Job::emitError(const GErrorPtr &err, Job::ErrorSeverity severity) {
|
||||
ErrorAction response = ErrorAction::CONTINUE;
|
||||
// if the error is already handled, don't emit it.
|
||||
if(err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_FAILED_HANDLED) {
|
||||
return response;
|
||||
}
|
||||
Q_EMIT error(err, severity, response);
|
||||
|
||||
if(severity == ErrorSeverity::CRITICAL || response == ErrorAction::ABORT) {
|
||||
cancel();
|
||||
}
|
||||
else if(response == ErrorAction::RETRY ) {
|
||||
/* If the job is already cancelled, retry is not allowed. */
|
||||
if(isCancelled() || (err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_CANCELLED)) {
|
||||
response = ErrorAction::CONTINUE;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
} // namespace Fm
|
120
src/core/job.h
Normal file
120
src/core/job.h
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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_JOB_H__
|
||||
#define __LIBFM_QT_FM_JOB_H__
|
||||
|
||||
#include <libfm/fm.h>
|
||||
#include <QObject>
|
||||
#include <QtGlobal>
|
||||
#include <QThread>
|
||||
#include <QRunnable>
|
||||
#include <memory>
|
||||
#include <gio/gio.h>
|
||||
#include "gobjectptr.h"
|
||||
#include "gioptrs.h"
|
||||
#include "../libfmqtglobals.h"
|
||||
|
||||
|
||||
namespace Fm {
|
||||
|
||||
/*
|
||||
* Fm::Job can be used in several different modes.
|
||||
* 1. run with QThreadPool::start()
|
||||
* 2. call runAsync(), which will create a new QThread and move the object to the thread.
|
||||
* 3. create a new QThread, and connect the started() signal to the slot Job::run()
|
||||
* 4. Directly call Job::run(), which executes synchrounously as a normal blocking call
|
||||
*/
|
||||
|
||||
class LIBFM_QT_API Job: public QObject, public QRunnable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
enum class ErrorAction{
|
||||
CONTINUE,
|
||||
RETRY,
|
||||
ABORT
|
||||
};
|
||||
|
||||
enum class ErrorSeverity {
|
||||
UNKNOWN,
|
||||
WARNING,
|
||||
MILD,
|
||||
MODERATE,
|
||||
SEVERE,
|
||||
CRITICAL
|
||||
};
|
||||
|
||||
explicit Job();
|
||||
|
||||
virtual ~Job();
|
||||
|
||||
bool isCancelled() const {
|
||||
return g_cancellable_is_cancelled(cancellable_.get());
|
||||
}
|
||||
|
||||
void runAsync(QThread::Priority priority = QThread::InheritPriority);
|
||||
|
||||
bool pause();
|
||||
|
||||
void resume();
|
||||
|
||||
const GCancellablePtr& cancellable() const {
|
||||
return cancellable_;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void cancelled();
|
||||
|
||||
void finished();
|
||||
|
||||
// this signal should be connected with Qt::BlockingQueuedConnection
|
||||
void error(const GErrorPtr& err, ErrorSeverity severity, ErrorAction& response);
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
void cancel();
|
||||
|
||||
void run() override;
|
||||
|
||||
protected:
|
||||
ErrorAction emitError(const GErrorPtr& err, ErrorSeverity severity = ErrorSeverity::MODERATE);
|
||||
|
||||
// all derived job subclasses should do their work in this method.
|
||||
virtual void exec() = 0;
|
||||
|
||||
private:
|
||||
static void _onCancellableCancelled(GCancellable* cancellable, Job* _this) {
|
||||
_this->onCancellableCancelled(cancellable);
|
||||
}
|
||||
|
||||
void onCancellableCancelled(GCancellable* /*cancellable*/) {
|
||||
Q_EMIT cancelled();
|
||||
}
|
||||
|
||||
private:
|
||||
bool paused_;
|
||||
GCancellablePtr cancellable_;
|
||||
gulong cancellableHandler_;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // __LIBFM_QT_FM_JOB_H__
|
26
src/core/job_p.h
Normal file
26
src/core/job_p.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef JOB_P_H
|
||||
#define JOB_P_H
|
||||
|
||||
#include <QThread>
|
||||
#include "job.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class JobThread: public QThread {
|
||||
Q_OBJECT
|
||||
public:
|
||||
JobThread(Job* job): job_{job} {
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void run() override {
|
||||
job_->run();
|
||||
}
|
||||
|
||||
Job* job_;
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // JOB_P_H
|
64
src/core/mimetype.cpp
Normal file
64
src/core/mimetype.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "mimetype.h"
|
||||
#include <cstring>
|
||||
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace Fm {
|
||||
|
||||
std::unordered_map<const char*, std::shared_ptr<const MimeType>, CStrHash, CStrEqual> MimeType::cache_;
|
||||
std::mutex MimeType::mutex_;
|
||||
|
||||
std::shared_ptr<const MimeType> MimeType::inodeDirectory_; // inode/directory
|
||||
std::shared_ptr<const MimeType> MimeType::inodeShortcut_; // inode/x-shortcut
|
||||
std::shared_ptr<const MimeType> MimeType::inodeMountPoint_; // inode/mount-point
|
||||
std::shared_ptr<const MimeType> MimeType::desktopEntry_; // application/x-desktop
|
||||
|
||||
|
||||
MimeType::MimeType(const char* typeName):
|
||||
name_{g_strdup(typeName)},
|
||||
desc_{nullptr} {
|
||||
|
||||
GObjectPtr<GIcon> gicon{g_content_type_get_icon(typeName), false};
|
||||
if(strcmp(typeName, "inode/directory") == 0)
|
||||
g_themed_icon_prepend_name(G_THEMED_ICON(gicon.get()), "folder");
|
||||
else if(g_content_type_can_be_executable(typeName))
|
||||
g_themed_icon_append_name(G_THEMED_ICON(gicon.get()), "application-x-executable");
|
||||
|
||||
icon_ = IconInfo::fromGIcon(gicon);
|
||||
}
|
||||
|
||||
MimeType::~MimeType () {
|
||||
}
|
||||
|
||||
//static
|
||||
std::shared_ptr<const MimeType> MimeType::fromName(const char* typeName) {
|
||||
std::shared_ptr<const MimeType> ret;
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto it = cache_.find(typeName);
|
||||
if(it == cache_.end()) {
|
||||
ret = std::make_shared<MimeType>(typeName);
|
||||
cache_.insert(std::make_pair(ret->name_.get(), ret));
|
||||
}
|
||||
else {
|
||||
ret = it->second;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// static
|
||||
std::shared_ptr<const MimeType> MimeType::guessFromFileName(const char* fileName) {
|
||||
gboolean uncertain;
|
||||
/* let skip scheme and host from non-native names */
|
||||
auto uri_scheme = g_strstr_len(fileName, -1, "://");
|
||||
if(uri_scheme)
|
||||
fileName = strchr(uri_scheme + 3, '/');
|
||||
if(fileName == nullptr)
|
||||
fileName = "unknown";
|
||||
auto type = CStrPtr{g_content_type_guess(fileName, nullptr, 0, &uncertain)};
|
||||
return fromName(type.get());
|
||||
}
|
||||
|
||||
} // namespace Fm
|
172
src/core/mimetype.h
Normal file
172
src/core/mimetype.h
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* fm-mime-type.h
|
||||
*
|
||||
* Copyright 2009 - 2012 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
||||
*
|
||||
* This file is a part of the Libfm library.
|
||||
*
|
||||
* 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 _FM2_MIME_TYPE_H_
|
||||
#define _FM2_MIME_TYPE_H_
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <cstring>
|
||||
#include <forward_list>
|
||||
#include <functional>
|
||||
|
||||
#include "cstrptr.h"
|
||||
#include "gobjectptr.h"
|
||||
#include "iconinfo.h"
|
||||
#include "thumbnailer.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API MimeType {
|
||||
public:
|
||||
friend class Thumbnailer;
|
||||
|
||||
explicit MimeType(const char* typeName);
|
||||
|
||||
MimeType() = delete;
|
||||
|
||||
~MimeType();
|
||||
|
||||
std::shared_ptr<const Thumbnailer> firstThumbnailer() const {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
return thumbnailers_.empty() ? nullptr : thumbnailers_.front();
|
||||
}
|
||||
|
||||
void forEachThumbnailer(std::function<bool(const std::shared_ptr<const Thumbnailer>&)> func) const {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
for(auto& thumbnailer: thumbnailers_) {
|
||||
if(func(thumbnailer)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::shared_ptr<const IconInfo>& icon() const {
|
||||
return icon_;
|
||||
}
|
||||
|
||||
const char* name() const {
|
||||
return name_.get();
|
||||
}
|
||||
|
||||
const char* desc() const {
|
||||
if(!desc_) {
|
||||
desc_ = CStrPtr{g_content_type_get_description(name_.get())};
|
||||
}
|
||||
return desc_.get();
|
||||
}
|
||||
|
||||
static std::shared_ptr<const MimeType> fromName(const char* typeName);
|
||||
|
||||
static std::shared_ptr<const MimeType> guessFromFileName(const char* fileName);
|
||||
|
||||
bool isUnknownType() const {
|
||||
return g_content_type_is_unknown(name_.get());
|
||||
}
|
||||
|
||||
bool isDesktopEntry() const {
|
||||
return this == desktopEntry().get();
|
||||
}
|
||||
|
||||
bool isText() const {
|
||||
return g_content_type_is_a(name_.get(), "text/plain");
|
||||
}
|
||||
|
||||
bool isImage() const {
|
||||
return !std::strncmp("image/", name_.get(), 6);
|
||||
}
|
||||
|
||||
bool isMountable() const {
|
||||
return this == inodeMountPoint().get();
|
||||
}
|
||||
|
||||
bool isShortcut() const {
|
||||
return this == inodeShortcut().get();
|
||||
}
|
||||
|
||||
bool isDir() const {
|
||||
return this == inodeDirectory().get();
|
||||
}
|
||||
|
||||
bool canBeExecutable() const {
|
||||
return g_content_type_can_be_executable(name_.get());
|
||||
}
|
||||
|
||||
static std::shared_ptr<const MimeType> inodeDirectory() { // inode/directory
|
||||
if(!inodeDirectory_)
|
||||
inodeDirectory_ = fromName("inode/directory");
|
||||
return inodeDirectory_;
|
||||
}
|
||||
|
||||
static std::shared_ptr<const MimeType> inodeShortcut() { // inode/x-shortcut
|
||||
if(!inodeShortcut_)
|
||||
inodeShortcut_ = fromName("inode/x-shortcut");
|
||||
return inodeShortcut_;
|
||||
}
|
||||
|
||||
static std::shared_ptr<const MimeType> inodeMountPoint() { // inode/mount-point
|
||||
if(!inodeMountPoint_)
|
||||
inodeMountPoint_ = fromName("inode/mount-point");
|
||||
return inodeMountPoint_;
|
||||
}
|
||||
|
||||
static std::shared_ptr<const MimeType> desktopEntry() { // application/x-desktop
|
||||
if(!desktopEntry_)
|
||||
desktopEntry_ = fromName("application/x-desktop");
|
||||
return desktopEntry_;
|
||||
}
|
||||
|
||||
private:
|
||||
void removeThumbnailer(std::shared_ptr<const Thumbnailer>& thumbnailer) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
thumbnailers_.remove(thumbnailer);
|
||||
}
|
||||
|
||||
void addThumbnailer(std::shared_ptr<const Thumbnailer> thumbnailer) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
thumbnailers_.push_front(std::move(thumbnailer));
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<const IconInfo> icon_;
|
||||
CStrPtr name_;
|
||||
mutable CStrPtr desc_;
|
||||
std::forward_list<std::shared_ptr<const Thumbnailer>> thumbnailers_;
|
||||
static std::unordered_map<const char*, std::shared_ptr<const MimeType>, CStrHash, CStrEqual> cache_;
|
||||
static std::mutex mutex_;
|
||||
|
||||
static std::shared_ptr<const MimeType> inodeDirectory_; // inode/directory
|
||||
static std::shared_ptr<const MimeType> inodeShortcut_; // inode/x-shortcut
|
||||
static std::shared_ptr<const MimeType> inodeMountPoint_; // inode/mount-point
|
||||
static std::shared_ptr<const MimeType> desktopEntry_; // application/x-desktop
|
||||
};
|
||||
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif
|
127
src/core/terminal.cpp
Normal file
127
src/core/terminal.cpp
Normal file
@ -0,0 +1,127 @@
|
||||
#include "terminal.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
#include <glib.h>
|
||||
#include <gio/gdesktopappinfo.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if !GLIB_CHECK_VERSION(2, 28, 0) && !HAVE_DECL_ENVIRON
|
||||
extern char** environ;
|
||||
#endif
|
||||
|
||||
static void child_setup(gpointer user_data) {
|
||||
/* Move child to grandparent group so it will not die with parent */
|
||||
setpgid(0, (pid_t)(gsize)user_data);
|
||||
}
|
||||
|
||||
bool launchTerminal(const char* programName, const FilePath& workingDir, Fm::GErrorPtr& error) {
|
||||
/* read system terminals file */
|
||||
GKeyFile* kf = g_key_file_new();
|
||||
if(!g_key_file_load_from_file(kf, LIBFM_QT_DATA_DIR "/terminals.list", G_KEY_FILE_NONE, &error)) {
|
||||
g_key_file_free(kf);
|
||||
return false;
|
||||
}
|
||||
auto launch = g_key_file_get_string(kf, programName, "launch", nullptr);
|
||||
auto desktop_id = g_key_file_get_string(kf, programName, "desktop_id", nullptr);
|
||||
|
||||
GDesktopAppInfo* appinfo = nullptr;
|
||||
if(desktop_id) {
|
||||
appinfo = g_desktop_app_info_new(desktop_id);
|
||||
}
|
||||
|
||||
const gchar* cmd;
|
||||
gchar* _cmd = nullptr;
|
||||
if(appinfo) {
|
||||
cmd = g_app_info_get_commandline(G_APP_INFO(appinfo));
|
||||
}
|
||||
else if(launch) {
|
||||
cmd = _cmd = g_strdup_printf("%s %s", programName, launch);
|
||||
}
|
||||
else {
|
||||
cmd = programName;
|
||||
}
|
||||
|
||||
#if 0 // FIXME: what's this?
|
||||
if(custom_args) {
|
||||
cmd = g_strdup_printf("%s %s", cmd, custom_args);
|
||||
g_free(_cmd);
|
||||
_cmd = (char*)cmd;
|
||||
}
|
||||
#endif
|
||||
|
||||
char** argv;
|
||||
int argc;
|
||||
if(!g_shell_parse_argv(cmd, &argc, &argv, nullptr)) {
|
||||
argv = nullptr;
|
||||
}
|
||||
g_free(_cmd);
|
||||
|
||||
if(appinfo) {
|
||||
g_object_unref(appinfo);
|
||||
}
|
||||
if(!argv) { /* parsing failed */
|
||||
return false;
|
||||
}
|
||||
char** envp;
|
||||
#if GLIB_CHECK_VERSION(2, 28, 0)
|
||||
envp = g_get_environ();
|
||||
#else
|
||||
envp = g_strdupv(environ);
|
||||
#endif
|
||||
|
||||
auto dir = workingDir ? workingDir.localPath() : nullptr;
|
||||
if(dir) {
|
||||
#if GLIB_CHECK_VERSION(2, 32, 0)
|
||||
envp = g_environ_setenv(envp, "PWD", dir.get(), TRUE);
|
||||
#else
|
||||
char** env = envp;
|
||||
|
||||
if(env) while(*env != nullptr) {
|
||||
if(strncmp(*env, "PWD=", 4) == 0) {
|
||||
break;
|
||||
}
|
||||
env++;
|
||||
}
|
||||
if(env == nullptr || *env == nullptr) {
|
||||
gint length;
|
||||
|
||||
length = envp ? g_strv_length(envp) : 0;
|
||||
envp = g_renew(gchar*, envp, length + 2);
|
||||
env = &envp[length];
|
||||
env[1] = nullptr;
|
||||
}
|
||||
else {
|
||||
g_free(*env);
|
||||
}
|
||||
*env = g_strdup_printf("PWD=%s", dir);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ret = g_spawn_async(dir.get(), argv, envp, G_SPAWN_SEARCH_PATH,
|
||||
child_setup, (gpointer)(gsize)getpgid(getppid()),
|
||||
nullptr, &error);
|
||||
g_strfreev(argv);
|
||||
g_strfreev(envp);
|
||||
g_key_file_free(kf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<CStrPtr> allKnownTerminals() {
|
||||
std::vector<CStrPtr> terminals;
|
||||
GKeyFile* kf = g_key_file_new();
|
||||
if(g_key_file_load_from_file(kf, LIBFM_QT_DATA_DIR "/terminals.list", G_KEY_FILE_NONE, nullptr)) {
|
||||
gsize n;
|
||||
auto programs = g_key_file_get_groups(kf, &n);
|
||||
terminals.reserve(n);
|
||||
for(auto name = programs; *name; ++name) {
|
||||
terminals.emplace_back(*name);
|
||||
}
|
||||
g_free(programs);
|
||||
}
|
||||
g_key_file_free(kf);
|
||||
return terminals;
|
||||
}
|
||||
|
||||
} // namespace Fm
|
17
src/core/terminal.h
Normal file
17
src/core/terminal.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef TERMINAL_H
|
||||
#define TERMINAL_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include "gioptrs.h"
|
||||
#include "filepath.h"
|
||||
#include <vector>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
LIBFM_QT_API bool launchTerminal(const char* programName, const FilePath& workingDir, GErrorPtr& error);
|
||||
|
||||
LIBFM_QT_API std::vector<CStrPtr> allKnownTerminals();
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // TERMINAL_H
|
140
src/core/thumbnailer.cpp
Normal file
140
src/core/thumbnailer.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
#include "thumbnailer.h"
|
||||
#include "mimetype.h"
|
||||
#include <string>
|
||||
#include <QDebug>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
std::mutex Thumbnailer::mutex_;
|
||||
std::vector<std::shared_ptr<Thumbnailer>> Thumbnailer::allThumbnailers_;
|
||||
|
||||
Thumbnailer::Thumbnailer(const char* id, GKeyFile* kf):
|
||||
id_{g_strdup(id)},
|
||||
try_exec_{g_key_file_get_string(kf, "Thumbnailer Entry", "TryExec", nullptr)},
|
||||
exec_{g_key_file_get_string(kf, "Thumbnailer Entry", "Exec", nullptr)} {
|
||||
}
|
||||
|
||||
CStrPtr Thumbnailer::commandForUri(const char* uri, const char* output_file, guint size) const {
|
||||
if(exec_) {
|
||||
/* 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
|
||||
*/
|
||||
GString* cmd_line = g_string_sized_new(1024);
|
||||
const char* p;
|
||||
for(p = exec_.get(); *p; ++p) {
|
||||
if(G_LIKELY(*p != '%')) {
|
||||
g_string_append_c(cmd_line, *p);
|
||||
}
|
||||
else {
|
||||
char* quoted;
|
||||
++p;
|
||||
switch(*p) {
|
||||
case '\0':
|
||||
break;
|
||||
case 's':
|
||||
g_string_append_printf(cmd_line, "%d", size);
|
||||
break;
|
||||
case 'i': {
|
||||
char* src_path = g_filename_from_uri(uri, nullptr, nullptr);
|
||||
if(src_path) {
|
||||
quoted = g_shell_quote(src_path);
|
||||
g_string_append(cmd_line, quoted);
|
||||
g_free(quoted);
|
||||
g_free(src_path);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
quoted = g_shell_quote(uri);
|
||||
g_string_append(cmd_line, quoted);
|
||||
g_free(quoted);
|
||||
break;
|
||||
case 'o':
|
||||
g_string_append(cmd_line, output_file);
|
||||
break;
|
||||
default:
|
||||
g_string_append_c(cmd_line, '%');
|
||||
if(*p != '%') {
|
||||
g_string_append_c(cmd_line, *p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return CStrPtr{g_string_free(cmd_line, FALSE)};
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Thumbnailer::run(const char* uri, const char* output_file, int size) const {
|
||||
auto cmd = commandForUri(uri, output_file, size);
|
||||
qDebug() << cmd.get();
|
||||
int status;
|
||||
bool ret = g_spawn_command_line_sync(cmd.get(), nullptr, nullptr, &status, nullptr);
|
||||
return ret && status == 0;
|
||||
}
|
||||
|
||||
static void find_thumbnailers_in_data_dir(std::unordered_map<std::string, const char*>& hash, const char* data_dir) {
|
||||
CStrPtr dir_path{g_build_filename(data_dir, "thumbnailers", nullptr)};
|
||||
GDir* dir = g_dir_open(dir_path.get(), 0, nullptr);
|
||||
if(dir) {
|
||||
const char* basename;
|
||||
while((basename = g_dir_read_name(dir)) != nullptr) {
|
||||
/* we only want filenames with .thumbnailer extension */
|
||||
if(G_LIKELY(g_str_has_suffix(basename, ".thumbnailer"))) {
|
||||
hash.insert(std::make_pair(basename, data_dir));
|
||||
}
|
||||
}
|
||||
g_dir_close(dir);
|
||||
}
|
||||
}
|
||||
|
||||
void Thumbnailer::loadAll() {
|
||||
const gchar* const* data_dirs = g_get_system_data_dirs();
|
||||
const gchar* const* data_dir;
|
||||
|
||||
/* use a temporary hash table to collect thumbnailer basenames
|
||||
* key: basename of thumbnailer entry file
|
||||
* value: data dir the thumbnailer entry file is in */
|
||||
std::unordered_map<std::string, const char*> hash;
|
||||
|
||||
/* load user-specific thumbnailers */
|
||||
find_thumbnailers_in_data_dir(hash, g_get_user_data_dir());
|
||||
|
||||
/* load system-wide thumbnailers */
|
||||
for(data_dir = data_dirs; *data_dir; ++data_dir) {
|
||||
find_thumbnailers_in_data_dir(hash, *data_dir);
|
||||
}
|
||||
|
||||
/* load all found thumbnailers */
|
||||
if(!hash.empty()) {
|
||||
std::lock_guard<std::mutex> lock{mutex_};
|
||||
GKeyFile* kf = g_key_file_new();
|
||||
for(auto& item: hash) {
|
||||
auto& base_name = item.first;
|
||||
auto& dir_path = item.second;
|
||||
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);
|
||||
}
|
||||
}
|
||||
g_strfreev(mime_types);
|
||||
}
|
||||
allThumbnailers_.push_back(std::move(thumbnailer));
|
||||
}
|
||||
}
|
||||
g_key_file_free(kf);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Fm
|
37
src/core/thumbnailer.h
Normal file
37
src/core/thumbnailer.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef FM2_THUMBNAILER_H
|
||||
#define FM2_THUMBNAILER_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include "cstrptr.h"
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class MimeType;
|
||||
|
||||
class LIBFM_QT_API Thumbnailer {
|
||||
public:
|
||||
explicit Thumbnailer(const char *id, GKeyFile *kf);
|
||||
|
||||
CStrPtr commandForUri(const char* uri, const char* output_file, guint size) const;
|
||||
|
||||
bool run(const char* uri, const char* output_file, int size) const;
|
||||
|
||||
static void loadAll();
|
||||
|
||||
private:
|
||||
CStrPtr id_;
|
||||
CStrPtr try_exec_; /* FIXME: is this useful? */
|
||||
CStrPtr exec_;
|
||||
std::vector<std::shared_ptr<const MimeType>> mimeTypes_;
|
||||
|
||||
static std::mutex mutex_;
|
||||
static std::vector<std::shared_ptr<Thumbnailer>> allThumbnailers_;
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_THUMBNAILER_H
|
266
src/core/thumbnailjob.cpp
Normal file
266
src/core/thumbnailjob.cpp
Normal file
@ -0,0 +1,266 @@
|
||||
#include "thumbnailjob.h"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <libexif/exif-loader.h>
|
||||
#include <QImageReader>
|
||||
#include <QDir>
|
||||
#include "thumbnailer.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
QThreadPool* ThumbnailJob::threadPool_ = nullptr;
|
||||
|
||||
bool ThumbnailJob::localFilesOnly_ = true;
|
||||
int ThumbnailJob::maxThumbnailFileSize_ = 0;
|
||||
|
||||
ThumbnailJob::ThumbnailJob(FileInfoList files, int size):
|
||||
files_{std::move(files)},
|
||||
size_{size},
|
||||
md5Calc_{g_checksum_new(G_CHECKSUM_MD5)} {
|
||||
}
|
||||
|
||||
ThumbnailJob::~ThumbnailJob() {
|
||||
g_checksum_free(md5Calc_);
|
||||
// qDebug("delete ThumbnailJob");
|
||||
}
|
||||
|
||||
void ThumbnailJob::exec() {
|
||||
for(auto& file: files_) {
|
||||
if(isCancelled()) {
|
||||
break;
|
||||
}
|
||||
auto image = loadForFile(file);
|
||||
Q_EMIT thumbnailLoaded(file, size_, image);
|
||||
results_.emplace_back(std::move(image));
|
||||
}
|
||||
}
|
||||
|
||||
QImage ThumbnailJob::readImageFromStream(GInputStream* stream, size_t len) {
|
||||
// FIXME: should we set a limit here? Otherwise if len is too large, we can run out of memory.
|
||||
std::unique_ptr<unsigned char[]> buffer{new unsigned char[len]}; // allocate enough buffer
|
||||
unsigned char* pbuffer = buffer.get();
|
||||
size_t totalReadSize = 0;
|
||||
while(!isCancelled() && totalReadSize < len) {
|
||||
size_t bytesToRead = totalReadSize + 4096 > len ? len - totalReadSize : 4096;
|
||||
gssize readSize = g_input_stream_read(stream, pbuffer, bytesToRead, cancellable_.get(), nullptr);
|
||||
if(readSize == 0) { // end of file
|
||||
break;
|
||||
}
|
||||
else if(readSize == -1) { // error
|
||||
return QImage();
|
||||
}
|
||||
totalReadSize += readSize;
|
||||
pbuffer += readSize;
|
||||
}
|
||||
QImage image;
|
||||
image.loadFromData(buffer.get(), totalReadSize);
|
||||
return image;
|
||||
}
|
||||
|
||||
QImage ThumbnailJob::loadForFile(const std::shared_ptr<const FileInfo> &file) {
|
||||
if(!file->canThumbnail()) {
|
||||
return QImage();
|
||||
}
|
||||
|
||||
// thumbnails are stored in $XDG_CACHE_HOME/thumbnails/large|normal|failed
|
||||
QString thumbnailDir{g_get_user_cache_dir()};
|
||||
thumbnailDir += "/thumbnails/";
|
||||
|
||||
// don't make thumbnails for files inside the thumbnail directory
|
||||
if(FilePath::fromLocalPath(thumbnailDir.toLocal8Bit().constData()).isParentOf(file->dirPath())) {
|
||||
return QImage();
|
||||
}
|
||||
|
||||
const char* subdir = size_ > 128 ? "large" : "normal";
|
||||
thumbnailDir += subdir;
|
||||
|
||||
// generate base name of the thumbnail => {md5 of uri}.png
|
||||
auto origPath = file->path();
|
||||
auto uri = origPath.uri();
|
||||
|
||||
char thumbnailName[32 + 5];
|
||||
// calculate md5 hash for the uri of the original file
|
||||
g_checksum_update(md5Calc_, reinterpret_cast<const unsigned char*>(uri.get()), -1);
|
||||
memcpy(thumbnailName, g_checksum_get_string(md5Calc_), 32);
|
||||
mempcpy(thumbnailName + 32, ".png", 5);
|
||||
g_checksum_reset(md5Calc_); // reset the checksum calculator for next use
|
||||
|
||||
QString thumbnailFilename = thumbnailDir;
|
||||
thumbnailFilename += '/';
|
||||
thumbnailFilename += thumbnailName;
|
||||
// qDebug() << "thumbnail:" << file->getName().c_str() << thumbnailFilename;
|
||||
|
||||
// try to load the thumbnail file if it exists
|
||||
QImage thumbnail{thumbnailFilename};
|
||||
if(thumbnail.isNull() || isThumbnailOutdated(file, thumbnail)) {
|
||||
// the existing thumbnail cannot be loaded, generate a new one
|
||||
|
||||
// create the thumbnail dir as needd (FIXME: Qt file I/O is slow)
|
||||
QDir().mkpath(thumbnailDir);
|
||||
|
||||
thumbnail = generateThumbnail(file, origPath, uri.get(), thumbnailFilename);
|
||||
}
|
||||
// resize to the size we need
|
||||
if(thumbnail.width() > size_ || thumbnail.height() > size_) {
|
||||
thumbnail = thumbnail.scaled(size_, size_, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
bool ThumbnailJob::isSupportedImageType(const std::shared_ptr<const MimeType>& mimeType) const {
|
||||
if(mimeType->isImage()) {
|
||||
auto supportedTypes = QImageReader::supportedMimeTypes();
|
||||
auto found = std::find(supportedTypes.cbegin(), supportedTypes.cend(), mimeType->name());
|
||||
if(found != supportedTypes.cend())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
bool ThumbnailJob::readJpegExif(GInputStream *stream, QImage& thumbnail, int& rotate_degrees) {
|
||||
/* try to extract thumbnails embedded in jpeg files */
|
||||
ExifLoader* exif_loader = exif_loader_new();
|
||||
while(!isCancelled()) {
|
||||
unsigned char buf[4096];
|
||||
gssize read_size = g_input_stream_read(stream, buf, 4096, cancellable_.get(), nullptr);
|
||||
if(read_size <= 0) { // EOF or error
|
||||
break;
|
||||
}
|
||||
if(exif_loader_write(exif_loader, buf, read_size) == 0) {
|
||||
break; // no more EXIF data
|
||||
}
|
||||
}
|
||||
ExifData* exif_data = exif_loader_get_data(exif_loader);
|
||||
exif_loader_unref(exif_loader);
|
||||
if(exif_data) {
|
||||
/* reference for EXIF orientation tag:
|
||||
* http://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;
|
||||
ExifByteOrder bo = exif_data_get_byte_order(exif_data);
|
||||
/* bo == EXIF_BYTE_ORDER_INTEL ; */
|
||||
orient = exif_get_short(orient_ent->data, bo);
|
||||
switch(orient) {
|
||||
case 1: /* no rotation */
|
||||
rotate_degrees = 0;
|
||||
break;
|
||||
case 8:
|
||||
rotate_degrees = 90;
|
||||
break;
|
||||
case 3:
|
||||
rotate_degrees = 180;
|
||||
break;
|
||||
case 6:
|
||||
rotate_degrees = 270;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(exif_data->data) { // if an embedded thumbnail is available, load it
|
||||
thumbnail.loadFromData(exif_data->data, exif_data->size);
|
||||
}
|
||||
exif_data_unref(exif_data);
|
||||
}
|
||||
return !thumbnail.isNull();
|
||||
}
|
||||
|
||||
QImage ThumbnailJob::generateThumbnail(const std::shared_ptr<const FileInfo>& file, const FilePath& origPath, const char* uri, const QString& thumbnailFilename) {
|
||||
QImage result;
|
||||
auto mime_type = file->mimeType();
|
||||
if(isSupportedImageType(mime_type)) {
|
||||
GFileInputStreamPtr ins{g_file_read(origPath.gfile().get(), cancellable_.get(), nullptr), false};
|
||||
if(!ins)
|
||||
return QImage();
|
||||
bool fromExif = false;
|
||||
int rotate_degrees = 0;
|
||||
if(strcmp(mime_type->name(), "image/jpeg") == 0) { // if this is a jpeg file
|
||||
// try to get the thumbnail embedded in EXIF data
|
||||
if(readJpegExif(G_INPUT_STREAM(ins.get()), result, rotate_degrees)) {
|
||||
fromExif = true;
|
||||
}
|
||||
}
|
||||
if(!fromExif) { // not able to generate a thumbnail from the EXIF data
|
||||
// load the original file and do the scaling ourselves
|
||||
g_seekable_seek(G_SEEKABLE(ins.get()), 0, G_SEEK_SET, cancellable_.get(), nullptr);
|
||||
result = readImageFromStream(G_INPUT_STREAM(ins.get()), file->size());
|
||||
}
|
||||
g_input_stream_close(G_INPUT_STREAM(ins.get()), nullptr, nullptr);
|
||||
|
||||
if(!result.isNull()) { // the image is successfully loaded
|
||||
// scale the image as needed
|
||||
int target_size = size_ > 128 ? 256 : 128;
|
||||
|
||||
// only scale the original image if it's too large
|
||||
if(result.width() > target_size || result.height() > target_size) {
|
||||
result = result.scaled(target_size, target_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
if(rotate_degrees != 0) {
|
||||
// degree values are 0, 90, 180, and 270 counterclockwise.
|
||||
// In Qt, QMatrix does rotation counterclockwise as well.
|
||||
// However, because the y axis of widget coordinate system is downward,
|
||||
// the real effect of the coordinate transformation becomes clockwise rotation.
|
||||
// So we need to use (360 - degree) here.
|
||||
// Quote from QMatrix API doc:
|
||||
// Note that if you apply a QMatrix to a point defined in widget
|
||||
// coordinates, the direction of the rotation will be clockwise because
|
||||
// the y-axis points downwards.
|
||||
result = result.transformed(QMatrix().rotate(360 - rotate_degrees));
|
||||
}
|
||||
|
||||
// save the generated thumbnail to disk (don't save png thumbnails for JPEG EXIF thumbnails since loading them is cheap)
|
||||
if(!fromExif) {
|
||||
result.setText("Thumb::MTime", QString::number(file->mtime()));
|
||||
result.setText("Thumb::URI", uri);
|
||||
result.save(thumbnailFilename, "PNG");
|
||||
}
|
||||
// qDebug() << "save thumbnail:" << thumbnailFilename;
|
||||
}
|
||||
}
|
||||
else { // the image format is not supported, try to find an external thumbnailer
|
||||
// try all available external thumbnailers for it until sucess
|
||||
int target_size = size_ > 128 ? 256 : 128;
|
||||
file->mimeType()->forEachThumbnailer([&](const std::shared_ptr<const Thumbnailer>& thumbnailer) {
|
||||
if(thumbnailer->run(uri, thumbnailFilename.toLocal8Bit().constData(), target_size)) {
|
||||
result = QImage(thumbnailFilename);
|
||||
}
|
||||
return !result.isNull(); // return true on success, and forEachThumbnailer() will stop.
|
||||
});
|
||||
|
||||
if(!result.isNull()) {
|
||||
// Some thumbnailers did not write the proper metadata required by the xdg spec to the output (such as evince-thumbnailer)
|
||||
// Here we waste some time to fix them so next time we don't need to re-generate these thumbnails. :-(
|
||||
bool changed = false;
|
||||
if(Q_UNLIKELY(result.text("Thumb::MTime").isEmpty())) {
|
||||
result.setText("Thumb::MTime", QString::number(file->mtime()));
|
||||
changed = true;
|
||||
}
|
||||
if(Q_UNLIKELY(result.text("Thumb::URI").isEmpty())) {
|
||||
result.setText("Thumb::URI", uri);
|
||||
changed = true;
|
||||
}
|
||||
if(Q_UNLIKELY(changed)) {
|
||||
// save the modified PNG file containing metadata to a file.
|
||||
result.save(thumbnailFilename, "PNG");
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QThreadPool* ThumbnailJob::threadPool() {
|
||||
if(Q_UNLIKELY(threadPool_ == nullptr)) {
|
||||
threadPool_ = new QThreadPool();
|
||||
threadPool_->setMaxThreadCount(1);
|
||||
}
|
||||
return threadPool_;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Fm
|
89
src/core/thumbnailjob.h
Normal file
89
src/core/thumbnailjob.h
Normal file
@ -0,0 +1,89 @@
|
||||
#ifndef FM2_THUMBNAILJOB_H
|
||||
#define FM2_THUMBNAILJOB_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include "fileinfo.h"
|
||||
#include "gioptrs.h"
|
||||
#include "job.h"
|
||||
#include <QThreadPool>
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API ThumbnailJob: public Job {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
explicit ThumbnailJob(FileInfoList files, int size);
|
||||
|
||||
~ThumbnailJob();
|
||||
|
||||
int size() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
static QThreadPool* threadPool();
|
||||
|
||||
|
||||
static void setLocalFilesOnly(bool value) {
|
||||
localFilesOnly_ = value;
|
||||
if(fm_config) {
|
||||
fm_config->thumbnail_local = localFilesOnly_;
|
||||
}
|
||||
}
|
||||
|
||||
static bool localFilesOnly() {
|
||||
return localFilesOnly_;
|
||||
}
|
||||
|
||||
static int maxThumbnailFileSize() {
|
||||
return maxThumbnailFileSize_;
|
||||
}
|
||||
|
||||
static void setMaxThumbnailFileSize(int size) {
|
||||
maxThumbnailFileSize_ = size;
|
||||
if(fm_config) {
|
||||
fm_config->thumbnail_max = maxThumbnailFileSize_;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<QImage>& results() const {
|
||||
return results_;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void thumbnailLoaded(const std::shared_ptr<const FileInfo>& file, int size, QImage thumbnail);
|
||||
|
||||
protected:
|
||||
|
||||
void exec() override;
|
||||
|
||||
private:
|
||||
|
||||
bool isSupportedImageType(const std::shared_ptr<const MimeType>& mimeType) const;
|
||||
|
||||
bool isThumbnailOutdated(const std::shared_ptr<const FileInfo>& file, const QImage& thumbnail) const;
|
||||
|
||||
QImage generateThumbnail(const std::shared_ptr<const FileInfo>& file, const FilePath& origPath, const char* uri, const QString& thumbnailFilename);
|
||||
|
||||
QImage readImageFromStream(GInputStream* stream, size_t len);
|
||||
|
||||
QImage loadForFile(const std::shared_ptr<const FileInfo>& file);
|
||||
|
||||
bool readJpegExif(GInputStream* stream, QImage& thumbnail, int& rotate_degrees);
|
||||
|
||||
private:
|
||||
FileInfoList files_;
|
||||
int size_;
|
||||
std::vector<QImage> results_;
|
||||
GCancellablePtr cancellable_;
|
||||
GChecksum* md5Calc_;
|
||||
|
||||
static QThreadPool* threadPool_;
|
||||
|
||||
static bool localFilesOnly_;
|
||||
static int maxThumbnailFileSize_;
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_THUMBNAILJOB_H
|
144
src/core/totalsizejob.cpp
Normal file
144
src/core/totalsizejob.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
#include "totalsizejob.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
static const char query_str[] =
|
||||
G_FILE_ATTRIBUTE_STANDARD_TYPE","
|
||||
G_FILE_ATTRIBUTE_STANDARD_NAME","
|
||||
G_FILE_ATTRIBUTE_STANDARD_IS_VIRTUAL","
|
||||
G_FILE_ATTRIBUTE_STANDARD_SIZE","
|
||||
G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE","
|
||||
G_FILE_ATTRIBUTE_ID_FILESYSTEM;
|
||||
|
||||
|
||||
TotalSizeJob::TotalSizeJob(FilePathList paths, Flags flags):
|
||||
paths_{std::move(paths)},
|
||||
flags_{flags},
|
||||
totalSize_{0},
|
||||
totalOndiskSize_{0},
|
||||
fileCount_{0},
|
||||
dest_fs_id{nullptr} {
|
||||
}
|
||||
|
||||
|
||||
void TotalSizeJob::exec(FilePath path, GFileInfoPtr inf) {
|
||||
GFileType type;
|
||||
const char* fs_id;
|
||||
bool descend;
|
||||
|
||||
_retry_query_info:
|
||||
if(!inf) {
|
||||
GErrorPtr err;
|
||||
inf = GFileInfoPtr {
|
||||
g_file_query_info(path.gfile().get(), query_str,
|
||||
(flags_ & FOLLOW_LINKS) ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable().get(), &err),
|
||||
false
|
||||
};
|
||||
if(!inf) {
|
||||
ErrorAction act = emitError( err, ErrorSeverity::MILD);
|
||||
err = nullptr;
|
||||
if(act == ErrorAction::RETRY) {
|
||||
goto _retry_query_info;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
type = g_file_info_get_file_type(inf.get());
|
||||
descend = true;
|
||||
|
||||
++fileCount_;
|
||||
/* SF bug #892: dir file size is not relevant in the summary */
|
||||
if(type != G_FILE_TYPE_DIRECTORY) {
|
||||
totalSize_ += g_file_info_get_size(inf.get());
|
||||
}
|
||||
totalOndiskSize_ += g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_STANDARD_ALLOCATED_SIZE);
|
||||
|
||||
/* 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) {
|
||||
/* files on different device requires an additional 'delete' for the source file. */
|
||||
++totalSize_; /* this is for the additional delete */
|
||||
++totalOndiskSize_;
|
||||
++fileCount_;
|
||||
}
|
||||
else {
|
||||
descend = false;
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
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);
|
||||
descend = (g_strcmp0(fs_id, dest_fs_id) == 0);
|
||||
}
|
||||
}
|
||||
fm_path_unref(fm_path);
|
||||
#endif
|
||||
inf = nullptr;
|
||||
|
||||
if(descend) {
|
||||
_retry_enum_children:
|
||||
GErrorPtr err;
|
||||
auto enu = GFileEnumeratorPtr {
|
||||
g_file_enumerate_children(path.gfile().get(), query_str,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
cancellable().get(), &err),
|
||||
false
|
||||
};
|
||||
if(enu) {
|
||||
while(!isCancelled()) {
|
||||
inf = GFileInfoPtr{g_file_enumerator_next_file(enu.get(), cancellable().get(), &err), false};
|
||||
if(inf) {
|
||||
FilePath child = path.child(g_file_info_get_name(inf.get()));
|
||||
exec(std::move(child), std::move(inf));
|
||||
}
|
||||
else {
|
||||
if(err) { /* error! */
|
||||
/* ErrorAction::RETRY is not supported */
|
||||
emitError( err, ErrorSeverity::MILD);
|
||||
err = nullptr;
|
||||
}
|
||||
else {
|
||||
/* EOF is reached, do nothing. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_file_enumerator_close(enu.get(), nullptr, nullptr);
|
||||
}
|
||||
else {
|
||||
ErrorAction act = emitError( err, ErrorSeverity::MILD);
|
||||
err = nullptr;
|
||||
if(act == ErrorAction::RETRY) {
|
||||
goto _retry_enum_children;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TotalSizeJob::exec() {
|
||||
for(auto& path : paths_) {
|
||||
exec(path, GFileInfoPtr{});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace Fm
|
56
src/core/totalsizejob.h
Normal file
56
src/core/totalsizejob.h
Normal file
@ -0,0 +1,56 @@
|
||||
#ifndef FM2_TOTALSIZEJOB_H
|
||||
#define FM2_TOTALSIZEJOB_H
|
||||
|
||||
#include "../libfmqtglobals.h"
|
||||
#include "fileoperationjob.h"
|
||||
#include "filepath.h"
|
||||
#include <cstdint>
|
||||
#include "gioptrs.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
class LIBFM_QT_API TotalSizeJob : public Fm::FileOperationJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Flags {
|
||||
DEFAULT = 0,
|
||||
FOLLOW_LINKS = 1 << 0,
|
||||
SAME_FS = 1 << 1,
|
||||
PREPARE_MOVE = 1 << 2,
|
||||
PREPARE_DELETE = 1 << 3
|
||||
};
|
||||
|
||||
explicit TotalSizeJob(FilePathList paths = FilePathList{}, Flags flags = DEFAULT);
|
||||
|
||||
std::uint64_t totalSize() const {
|
||||
return totalSize_;
|
||||
}
|
||||
|
||||
std::uint64_t totalOnDiskSize() const {
|
||||
return totalOndiskSize_;
|
||||
}
|
||||
|
||||
unsigned int fileCount() const {
|
||||
return fileCount_;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void exec() override;
|
||||
|
||||
private:
|
||||
void exec(FilePath path, GFileInfoPtr inf);
|
||||
|
||||
private:
|
||||
FilePathList paths_;
|
||||
|
||||
int flags_;
|
||||
std::uint64_t totalSize_;
|
||||
std::uint64_t totalOndiskSize_;
|
||||
unsigned int fileCount_;
|
||||
const char* dest_fs_id;
|
||||
};
|
||||
|
||||
} // namespace Fm
|
||||
|
||||
#endif // FM2_TOTALSIZEJOB_H
|
73
src/core/trashjob.cpp
Normal file
73
src/core/trashjob.cpp
Normal file
@ -0,0 +1,73 @@
|
||||
#include "trashjob.h"
|
||||
|
||||
namespace Fm {
|
||||
|
||||
TrashJob::TrashJob(const FilePathList& paths): paths_{paths} {
|
||||
}
|
||||
|
||||
TrashJob::TrashJob(const FilePathList&& paths): paths_{paths} {
|
||||
}
|
||||
|
||||
void TrashJob::exec() {
|
||||
setTotalAmount(paths_.size(), paths_.size());
|
||||
Q_EMIT preparedToRun();
|
||||
|
||||
/* FIXME: we shouldn't trash a file already in trash:/// */
|
||||
for(auto& path : paths_) {
|
||||
if(isCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
bool ret = FALSE;
|
||||
if(fm_config->no_usb_trash) {
|
||||
err.reset();
|
||||
GMountPtr mnt{g_file_find_enclosing_mount(gf, nullptr, &err), false};
|
||||
if(mnt) {
|
||||
ret = g_mount_can_unmount(mnt.get()); /* TRUE if it's removable media */
|
||||
if(ret) {
|
||||
unsupportedFiles_.push_back(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!ret) {
|
||||
err.reset();
|
||||
ret = g_file_trash(gf, cancellable().get(), &err);
|
||||
}
|
||||
if(!ret) {
|
||||
/* 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);
|
||||
}
|
||||
else {
|
||||
ErrorAction act = emitError(err, ErrorSeverity::MODERATE);
|
||||
if(act == ErrorAction::RETRY) {
|
||||
err.reset();
|
||||
}
|
||||
else if(act == ErrorAction::ABORT) {
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
addFinishedAmount(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace Fm
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user