diff --git a/AUTHORS b/AUTHORS deleted file mode 100644 index a95b73b..0000000 --- a/AUTHORS +++ /dev/null @@ -1,6 +0,0 @@ -Upstream Authors: - LXQt team: https://lxqt.org - Hong Jen Yee (PCMan) - -Copyright: - Copyright (c) 2013-2018 LXQt team diff --git a/CHANGELOG b/CHANGELOG deleted file mode 100644 index 79cc040..0000000 --- a/CHANGELOG +++ /dev/null @@ -1,678 +0,0 @@ - -libfm-qt-0.13.1 / 2018-06-02 -============================ - - * Bump patch version to 1 - * It's supposedly fixed in Qt-5.11.1 - * Disconnect old source model - -0.13.0 / 2018-05-21 -=================== - - * Release 0.13.0: Update changelog - * Bumped minor version to 13 - * Fixed shortcut detection - * Delete CachedFolderModel immediately on unreffing it - * Fix crashes in new templates code - * Remove the C++ wrapper for the old FmPath struct in libfm. - * Avoid using FmPath from libfm in FmSearch. - * Removed redundant code - * Modify Fm::BasicFileLauncher APIs to use Fm::FileInfoPtr consistently. - * Add typedef FileInfoPtr for std::shared_ptr since it's so frequently used. - * Correctly handle the mounting of mountable paths and correctly parse the target URIs of shortcuts. - * Add new APIs Fm::MountOperation::mountEnclosingVolume() and Fm::MountOperation::mountMountable() and deprecate Fm::MountOperation::mount(). - * Also cover local shortcuts - * Added "reject" to exec dialog - * Delete some libfm compatibility wrappers. - * Delete the unused old FmTemplate wrapper. - * Create the template items in the "Create New" menu with the new C++ file template APIs. - * Port template support to C++ (Fm::Templates class). - * Add convinient functions in Fm::FileOperation to set dest paths for file transfer jobs. - * Made job async again - * Fixed launching of desktop files - * Cleanup - * CMake: Prevent in-source builds - * Fix Fm::FileInfo::isDir() and isExecutableType() to make its behavior consistent with libfm. - * Port file lancher to C++ and Implement Fm::BasicFileLauncher base class. * Migrate Fm::FileLauncher to the new C++ implementation. - * Port archiver integration to C++ (#182) - * Fully port all file operations to C++ 11 (#181) - * Add kitty to terminals.list - * Italic font for hidden items - * Select multiple files - * No visible cursor in File Properties labels - * Just corrected a misspelling - * Elided labels for file operation dialog - * fix namespace, fixes lxqt/libfm-qt/issues/174 - * Misc link fixes - * Fixes some pathes after repo move - * Fix a cause of crash in `AppChooserComboBox` - * build: Bump version - * Drop Fm::IconTheme - * iconinfo: Properly handle multiple names - * Some code cleanup - * Fixed custom action execution mode - * Don't drop on files - * Prevent possible c++11 range-loop container detach - * See `.bak` and `.old` as backup extensions; also follow GLib - * Ensure that rename editor has opaque background - * Use special/custom folder icons for bookmarks - * Added two missing cases of `mapFromSource()` - * Support hiding items in Places side-pane - * Fixed sorting by type/owner - * Fix lambda connections in filedialog (#159) - * Drop Q_FOREACH - * Fix memory leak in thumbnail loading (#150) - * Be more tolerant - * Fix the "Folders" key in custom actions - * Add Group column and don't use owner's full name - * Fix comparison of integers of different signs - * Guarantee 64-bit time attributes (#148) - * Added a proxy setting for backup as hidden (#145) - * Fixed the logic of queued deletion - * Track folders containing cut files only with QString - * Copy selected pathbar text to selection clipboard - * Smooth scrolling for icon and thumbnail views - * cmake: Handle CMP0071 - * Prepare libfm-qt for bulk rename - * No change queue of files in the deletion queue - * FileInfo: Fix potential SEGFAULT - * Fix two devices for one mount - * Always wait for the folder to load before selecting - * Really cancel multiple renaming on cancelling - * Prevent a potential crash in xdndworkaround - * Fix wrong gray-out of cut files - * Merge side-pane with its surroundings - * Prompt dialog specifically for desktop files - * Remove unnecessary noise - -0.12.0 / 2017-10-21 -=================== - - * Release 0.12.0: Update changelog - * Add data transferred to file operation dialog. - * Bump versions - * Disable context-menu actions that cannot be used - * 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 ) - * 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 / 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. diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index b1aff79..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,90 +0,0 @@ -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 13) -set(LIBFM_QT_VERSION_PATCH 1) -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" -# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info -# https://www.sourceware.org/autobook/autobook/autobook_91.html -# http://pusling.com/blog/?p=352 -# Actually, libtool uses different ways on different operating systems. So there is no -# universal way to translate a libtool version-info to a cmake version. -# We use "(current-age).age.revision" as the cmake version. -# current: 5, revision: 0, age: 0 => version: 5.0.0 -set(LIBFM_QT_LIB_VERSION "5.0.0") -set(LIBFM_QT_LIB_SOVERSION "5") - -set(REQUIRED_QT_VERSION "5.7.1") -set(REQUIRED_LIBFM_VERSION "1.2.0") -set(REQUIRED_LIBMENUCACHE_VERSION "0.4.0") -set(REQUIRED_LXQT_BUILD_TOOLS_VERSION "0.5.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(LXQtPreventInSourceBuilds) -include(LXQtTranslateTs) -include(LXQtTranslateDesktop) -include(LXQtCompilerSettings NO_POLICY_SCOPE) - -set(CMAKE_AUTOMOC TRUE) -set(CMAKE_AUTOUIC ON) -set(CMAKE_INCLUDE_CURRENT_DIR ON) - -write_basic_package_version_file( - "${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: -# https://majewsky.wordpress.com/2010/08/14/tip-of-the-day-cmake-and-doxygen/ -option(BUILD_DOCUMENTATION "Use Doxygen to create the HTML based API documentation" OFF) -if(BUILD_DOCUMENTATION) - find_package(Doxygen REQUIRED) - 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() diff --git a/Doxyfile.in b/Doxyfile.in deleted file mode 100644 index 9b102f0..0000000 --- a/Doxyfile.in +++ /dev/null @@ -1,1890 +0,0 @@ -# Doxyfile 1.8.4 - -# This file describes the settings to be used by the documentation system -# doxygen (www.doxygen.org) for a project. -# -# All text after a double hash (##) is considered a comment and is placed -# in front of the TAG it is preceding . -# All text after a hash (#) is considered a comment and will be ignored. -# The format is: -# TAG = value [value, ...] -# For lists items can also be appended using: -# TAG + = value [value, ...] -# Values that contain spaces should be placed between quotes (" "). - -#--------------------------------------------------------------------------- -# Project related configuration options -#--------------------------------------------------------------------------- - -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all -# text before the first occurrence of this tag. Doxygen uses libiconv (or the -# iconv built into libc) for the transcoding. See -# https://www.gnu.org/software/libiconv for the list of possible encodings. - -DOXYFILE_ENCODING = UTF-8 - -# The PROJECT_NAME tag is a single word (or sequence of words) that should -# identify the project. Note that if you do not use Doxywizard you need -# to put quotes around the project name if it contains spaces. - -PROJECT_NAME = "libfm-qt" - -# The PROJECT_NUMBER tag can be used to enter a project or revision number. -# This could be handy for archiving the generated documentation or -# if some version control system is used. - -PROJECT_NUMBER = - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer -# a quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = - -# With the PROJECT_LOGO tag one can specify an logo or icon that is -# included in the documentation. The maximum height of the logo should not -# exceed 55 pixels and the maximum width should not exceed 200 pixels. -# Doxygen will copy the logo to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) -# base path where the generated documentation will be put. -# If a relative path is entered, it will be relative to the location -# where doxygen was started. If left blank the current directory will be used. - -OUTPUT_DIRECTORY = @PROJECT_BINARY_DIR@/docs - -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create -# 4096 sub-directories (in 2 levels) under the output directory of each output -# format and will distribute the generated files over these directories. -# Enabling this option can be useful when feeding doxygen a huge amount of -# source files, where putting all generated files in the same directory would -# otherwise cause performance problems for the file system. - -CREATE_SUBDIRS = NO - -# The OUTPUT_LANGUAGE tag is used to specify the language in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all constant output in the proper language. -# The default language is English, other supported languages are: -# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, -# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, -# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English -# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, -# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, -# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. - -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will -# include brief member descriptions after the members that are listed in -# the file and class documentation (similar to JavaDoc). -# Set to NO to disable this. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend -# the brief description of a member or function before the detailed description. -# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator -# that is used to form the text in various listings. Each string -# in this list, if found as the leading text of the brief description, will be -# stripped from the text and the result after processing the whole list, is -# used as the annotated text. Otherwise, the brief description is used as-is. -# If left blank, the following values are used ("$name" is automatically -# replaced with the name of the entity): "The $name class" "The $name widget" -# "The $name file" "is" "provides" "specifies" "contains" -# "represents" "a" "an" "the" - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# Doxygen will generate a detailed section even if there is only a brief -# description. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full -# path before files name in the file list and in the header files. If set -# to NO the shortest path that makes the file name unique will be used. - -FULL_PATH_NAMES = NO - -# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag -# can be used to strip a user-defined part of the path. Stripping is -# only done if one of the specified strings matches the left-hand part of -# the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the -# path to strip. Note that you specify absolute paths here, but also -# relative paths, which will be relative from the directory where doxygen is -# started. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of -# the path mentioned in the documentation of a class, which tells -# the reader which header file to include in order to use a class. -# If left blank only the name of the header file containing the class -# definition is used. Otherwise one should specify the include paths that -# are normally passed to the compiler using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter -# (but less readable) file names. This can be useful if your file system -# doesn't support long names like on DOS, Mac, or CD-ROM. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen -# will interpret the first line (until the first dot) of a JavaDoc-style -# comment as the brief description. If set to NO, the JavaDoc -# comments will behave just like regular Qt-style comments -# (thus requiring an explicit @brief command for a brief description.) - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then Doxygen will -# interpret the first line (until the first dot) of a Qt-style -# comment as the brief description. If set to NO, the comments -# will behave just like regular Qt-style comments (thus requiring -# an explicit \brief command for a brief description.) - -QT_AUTOBRIEF = NO - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen -# treat a multi-line C++ special comment block (i.e. a block of //! or /// -# comments) as a brief description. This used to be the default behaviour. -# The new default is to treat a multi-line C++ comment block as a detailed -# description. Set this tag to YES if you prefer the old behaviour instead. - -MULTILINE_CPP_IS_BRIEF = NO - -# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented -# member inherits the documentation from any documented member that it -# re-implements. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce -# a new page for each member. If set to NO, the documentation of a member will -# be part of the file/class/namespace that contains it. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. -# Doxygen uses this value to replace tabs by spaces in code fragments. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that acts -# as commands in the documentation. An alias has the form "name = value". -# For example adding "sideeffect = \par Side Effects:\n" will allow you to -# put the command \sideeffect (or @sideeffect) in the documentation, which -# will result in a user-defined paragraph with heading "Side Effects:". -# You can put \n's in the value part of an alias to insert newlines. - -ALIASES = - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name = value". For example adding -# "class = itcl::class" will allow you to use the command class in the -# itcl::class meaning. - -TCL_SUBST = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C -# sources only. Doxygen will then generate output that is more tailored for C. -# For instance, some of the names that are used will be different. The list -# of all members will be omitted, etc. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java -# sources only. Doxygen will then generate output that is more tailored for -# Java. For instance, namespaces will be presented as packages, qualified -# scopes will look different, etc. - -OPTIMIZE_OUTPUT_JAVA = NO - -# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran -# sources only. Doxygen will then generate output that is more tailored for -# Fortran. - -OPTIMIZE_FOR_FORTRAN = NO - -# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL -# sources. Doxygen will then generate output that is tailored for -# VHDL. - -OPTIMIZE_OUTPUT_VHDL = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext = language, where ext is a file extension, -# and language is one of the parsers supported by doxygen: IDL, Java, -# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, -# C++. For instance to make doxygen treat .inc files as Fortran files (default -# is PHP), and .f files as C (default is Fortran), use: inc = Fortran f = C. Note -# that for custom extensions you also need to set FILE_PATTERNS otherwise the -# files are not read by doxygen. - -EXTENSION_MAPPING = - -# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all -# comments according to the Markdown format, which allows for more readable -# documentation. See https://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you -# can mix doxygen, HTML, and XML commands with Markdown formatting. -# Disable only in case of backward compatibilities issues. - -MARKDOWN_SUPPORT = YES - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. - -AUTOLINK_SUPPORT = YES - -# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want -# to include (a tag file for) the STL sources as input, then you should -# set this tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. -# func(std::string) {}). This also makes the inheritance and collaboration -# diagrams that involve STL classes more complete and accurate. - -BUILTIN_STL_SUPPORT = NO - -# If you use Microsoft's C++/CLI language, you should set this option to YES to -# enable parsing support. - -CPP_CLI_SUPPORT = NO - -# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. -# Doxygen will parse them like normal C++ but will assume all classes use public -# instead of private inheritance when no explicit protection keyword is present. - -SIP_SUPPORT = NO - -# For Microsoft's IDL there are propget and propput attributes to indicate -# getter and setter methods for a property. Setting this option to YES (the -# default) will make doxygen replace the get and set methods by a property in -# the documentation. This will only work if the methods are indeed getting or -# setting a simple type. If this is not the case, or you want to show the -# methods anyway, you should set this option to NO. - -IDL_PROPERTY_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES (the default) to allow class member groups of -# the same type (for instance a group of public functions) to be put as a -# subgroup of that type (e.g. under the Public Functions section). Set it to -# NO to prevent subgrouping. Alternatively, this can be done per class using -# the \nosubgrouping command. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and -# unions are shown inside the group in which they are included (e.g. using -# @ingroup) instead of on a separate page (for HTML and Man pages) or -# section (for LaTeX and RTF). - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and -# unions with only public data fields or simple typedef fields will be shown -# inline in the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO (the default), structs, classes, and unions are shown on a separate -# page (for HTML and Man pages) or section (for LaTeX and RTF). - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum -# is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically -# be useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. - -TYPEDEF_HIDES_STRUCT = NO - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can -# be an expensive process and often the same symbol appear multiple times in -# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too -# small doxygen will become slower. If the cache is too large, memory is wasted. -# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid -# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 -# symbols. - -LOOKUP_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. -# Private class members and static file members will be hidden unless -# the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class -# will be included in the documentation. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal -# scope will be included in the documentation. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file -# will be included in the documentation. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) -# defined locally in source files will be included in the documentation. -# If set to NO only classes defined in header files are included. - -EXTRACT_LOCAL_CLASSES = YES - -# This flag is only useful for Objective-C code. When set to YES local -# methods, which are defined in the implementation section but not in -# the interface are included in the documentation. -# If set to NO (the default) only methods in the interface are included. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base -# name of the file that contains the anonymous namespace. By default -# anonymous namespaces are hidden. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all -# undocumented members of documented classes, files or namespaces. -# If set to NO (the default) these members will be included in the -# various overviews, but no documentation section is generated. -# This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_MEMBERS = NO - -# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. -# If set to NO (the default) these classes will be included in the various -# overviews. This option has no effect if EXTRACT_ALL is enabled. - -HIDE_UNDOC_CLASSES = NO - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all -# friend (class|struct|union) declarations. -# If set to NO (the default) these declarations will be included in the -# documentation. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any -# documentation blocks found inside the body of a function. -# If set to NO (the default) these blocks will be appended to the -# function's detailed documentation block. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation -# that is typed after a \internal command is included. If the tag is set -# to NO (the default) then the documentation will be excluded. -# Set it to YES to include the internal documentation. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate -# file names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen -# will show members with their full class and namespace scopes in the -# documentation. If set to YES the scope will be hidden. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen -# will put a list of the files that are included by a file in the documentation -# of that file. - -SHOW_INCLUDE_FILES = YES - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen -# will list include files with double quotes in the documentation -# rather than with sharp brackets. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] -# is inserted in the documentation for inline members. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen -# will sort the (detailed) documentation of file and class members -# alphabetically by member name. If set to NO the members will appear in -# declaration order. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the -# brief documentation of file, namespace and class members alphabetically -# by member name. If set to NO (the default) the members will appear in -# declaration order. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen -# will sort the (brief and detailed) documentation of class members so that -# constructors and destructors are listed first. If set to NO (the default) -# the constructors will appear in the respective orders defined by -# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. -# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO -# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the -# hierarchy of group names into alphabetical order. If set to NO (the default) -# the group names will appear in their defined order. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be -# sorted by fully-qualified names, including namespaces. If set to -# NO (the default), the class list will be sorted only by class name, -# not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the -# alphabetical list. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to -# do proper type resolution of all parameters of a function it will reject a -# match between the prototype and the implementation of a member function even -# if there is only one candidate or it is obvious which candidate to choose -# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen -# will still accept a match between prototype and implementation in such cases. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable (YES) or -# disable (NO) the todo list. This list is created by putting \todo -# commands in the documentation. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable (YES) or -# disable (NO) the test list. This list is created by putting \test -# commands in the documentation. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable (YES) or -# disable (NO) the bug list. This list is created by putting \bug -# commands in the documentation. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or -# disable (NO) the deprecated list. This list is created by putting -# \deprecated commands in the documentation. - -GENERATE_DEPRECATEDLIST = YES - -# The ENABLED_SECTIONS tag can be used to enable conditional -# documentation sections, marked by \if section-label ... \endif -# and \cond section-label ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines -# the initial value of a variable or macro consists of for it to appear in -# the documentation. If the initializer consists of more lines than specified -# here it will be hidden. Use a value of 0 to hide initializers completely. -# The appearance of the initializer of individual variables and macros in the -# documentation can be controlled using \showinitializer or \hideinitializer -# command in the documentation regardless of this setting. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated -# at the bottom of the documentation of classes and structs. If set to YES the -# list will mention the files that were used to generate the documentation. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. -# This will remove the Files entry from the Quick Index and from the -# Folder Tree View (if specified). The default is YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the -# Namespaces page. -# This will remove the Namespaces entry from the Quick Index -# and from the Folder Tree View (if specified). The default is YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command , where is the value of -# the FILE_VERSION_FILTER tag, and is the name of an input file -# provided by doxygen. Whatever the program writes to standard output -# is used as the file version. See the manual for examples. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. -# You can optionally specify a file name after the option, if omitted -# DoxygenLayout.xml will be used as the name of the layout file. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files -# containing the references data. This must be a list of .bib files. The -# .bib extension is automatically appended if omitted. Using this command -# requires the bibtex tool to be installed. See also -# https://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style -# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this -# feature you need bibtex and perl available in the search path. Do not use -# file names with spaces, bibtex cannot handle them. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated -# by doxygen. Possible values are YES and NO. If left blank NO is used. - -QUIET = NO - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated by doxygen. Possible values are YES and NO. If left blank -# NO is used. - -WARNINGS = YES - -# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings -# for undocumented members. If EXTRACT_ALL is set to YES then this flag will -# automatically be disabled. - -WARN_IF_UNDOCUMENTED = YES - -# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some -# parameters in a documented function, or documenting parameters that -# don't exist or using markup commands wrongly. - -WARN_IF_DOC_ERROR = YES - -# The WARN_NO_PARAMDOC option can be enabled to get warnings for -# functions that are documented, but have no documentation for their parameters -# or return value. If set to NO (the default) doxygen will only warn about -# wrong or incomplete parameter documentation, but not about the absence of -# documentation. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that -# doxygen can produce. The string should contain the $file, $line, and $text -# tags, which will be replaced by the file and line number from which the -# warning originated and the warning text. Optionally the format may contain -# $version, which will be replaced by the version of the file (if it could -# be obtained via FILE_VERSION_FILTER) - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning -# and error messages should be written. If left blank the output is written -# to stderr. - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag can be used to specify the files and/or directories that contain -# documented source files. You may enter file names like "myfile.cpp" or -# directories like "/usr/src/myproject". Separate the files or directories -# with spaces. - -INPUT = "@PROJECT_SOURCE_DIR@/src" - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is -# also the default input encoding. Doxygen uses libiconv (or the iconv built -# into libc) for the transcoding. See https://www.gnu.org/software/libiconv for -# the list of possible encodings. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank the following patterns are tested: -# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh -# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py -# *.f90 *.f *.for *.vhd *.vhdl - -FILE_PATTERNS = - -# The RECURSIVE tag can be used to turn specify whether or not subdirectories -# should be searched for input files as well. Possible values are YES and NO. -# If left blank NO is used. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# Note that relative paths are relative to the directory from which doxygen is -# run. - -EXCLUDE = - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. Note that the wildcards are matched -# against the file with absolute path, so to exclude all test directories -# for example use the pattern */test/* - -EXCLUDE_PATTERNS = */*_p.h - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test - -EXCLUDE_SYMBOLS = LibFmQtData - -# The EXAMPLE_PATH tag can be used to specify one or more files or -# directories that contain example code fragments that are included (see -# the \include command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp -# and *.h) to filter out the source-files in the directories. If left -# blank all files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude -# commands irrespective of the value of the RECURSIVE tag. -# Possible values are YES and NO. If left blank NO is used. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or -# directories that contain image that are included in the documentation (see -# the \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command , where -# is the value of the INPUT_FILTER tag, and is the name of an -# input file. Doxygen will then use the output that the filter program writes -# to standard output. -# If FILTER_PATTERNS is specified, this tag will be ignored. -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. -# Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. -# The filters are a list of the form: -# pattern = filter (like *.cpp = my_cpp_filter). See INPUT_FILTER for further -# info on how filters are used. If FILTER_PATTERNS is empty or if -# non of the patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER) will be used to filter the input files when producing source -# files to browse (i.e. when SOURCE_BROWSER is set to YES). - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) -# and it is also possible to disable source filtering for a specific pattern -# using *.ext = (so without naming a filter). This option only has effect when -# FILTER_SOURCE_FILES is enabled. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will -# be generated. Documented entities will be cross-referenced with these sources. -# Note: To get rid of all source code in the generated output, make sure also -# VERBATIM_HEADERS is set to NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body -# of functions and classes directly in the documentation. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct -# doxygen to hide any special comment blocks from generated source code -# fragments. Normal C, C++ and Fortran comments will always remain visible. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES -# then for each documented function all documented -# functions referencing it will be listed. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES -# then for each documented function all documented entities -# called/used by that function will be listed. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) -# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from -# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will -# link to the source code. -# Otherwise they will link to the documentation. - -REFERENCES_LINK_SOURCE = YES - -# If the USE_HTAGS tag is set to YES then the references to source code -# will point to the HTML generated by the htags(1) tool instead of doxygen -# built-in source browser. The htags tool is part of GNU's global source -# tagging system (see http://www.gnu.org/software/global/global.html). You -# will need version 4.8.6 or higher. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen -# will generate a verbatim copy of the header file for each class for -# which an include is specified. Set to NO to disable this. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index -# of all compounds will be generated. Enable this if the project -# contains a lot of classes, structs, unions or interfaces. - -ALPHABETICAL_INDEX = YES - -# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then -# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns -# in which this list will be split (can be a number in the range [1..20]) - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all -# classes will be put under the same header in the alphabetical index. -# The IGNORE_PREFIX tag can be used to specify one or more prefixes that -# should be ignored while generating the index headers. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES (the default) Doxygen will -# generate HTML output. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `html' will be used as the default path. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for -# each generated HTML page (for example: .htm,.php,.asp). If it is left blank -# doxygen will generate files with .html extension. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a personal HTML header for -# each generated HTML page. If it is left blank doxygen will generate a -# standard header. Note that when using a custom header you are responsible -# for the proper inclusion of any scripts and style sheets that doxygen -# needs, which is dependent on the configuration options used. -# It is advised to generate a default header using "doxygen -w html -# header.html footer.html stylesheet.css YourConfigFile" and then modify -# that header. Note that the header is subject to change so you typically -# have to redo this when upgrading to a newer version of doxygen or when -# changing the value of configuration settings such as GENERATE_TREEVIEW! - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a personal HTML footer for -# each generated HTML page. If it is left blank doxygen will generate a -# standard footer. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading -# style sheet that is used by each HTML page. It can be used to -# fine-tune the look of the HTML output. If left blank doxygen will -# generate a default style sheet. Note that it is recommended to use -# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this -# tag will in the future become obsolete. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional -# user-defined cascading style sheet that is included after the standard -# style sheets created by doxygen. Using this option one can overrule -# certain style aspects. This is preferred over using HTML_STYLESHEET -# since it does not replace the standard style sheet and is therefor more -# robust against future updates. Doxygen will copy the style sheet file to -# the output directory. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that -# the files will be copied as-is; there are no commands or markers available. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. -# Doxygen will adjust the colors in the style sheet and background images -# according to this color. Hue is specified as an angle on a colorwheel, -# see https://en.wikipedia.org/wiki/Hue for more information. -# For instance the value 0 represents red, 60 is yellow, 120 is green, -# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. -# The allowed range is 0 to 359. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of -# the colors in the HTML output. For a value of 0 the output will use -# grayscales only. A value of 255 will produce the most vivid colors. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to -# the luminance component of the colors in the HTML output. Values below -# 100 gradually make the output lighter, whereas values above 100 make -# the output darker. The value divided by 100 is the actual gamma applied, -# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, -# and 100 does not change the gamma. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting -# this to NO can help when comparing the output of multiple runs. - -HTML_TIMESTAMP = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of -# entries shown in the various tree structured indices initially; the user -# can expand and collapse entries dynamically later on. Doxygen will expand -# the tree to such a level that at most the specified number of entries are -# visible (unless a fully collapsed tree already exceeds this amount). -# So setting the number of entries 1 will produce a full collapsed tree by -# default. 0 is a special value representing an infinite number of entries -# and will result in a full expanded tree by default. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files -# will be generated that can be used as input for Apple's Xcode 3 -# integrated development environment, introduced with OSX 10.5 (Leopard). -# To create a documentation set, doxygen will generate a Makefile in the -# HTML output directory. Running make will produce the docset in that -# directory and running "make install" will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find -# it at startup. -# See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. - -GENERATE_DOCSET = NO - -# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the -# feed. A documentation feed provides an umbrella under which multiple -# documentation sets from a single provider (such as a company or product suite) -# can be grouped. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that -# should uniquely identify the documentation set bundle. This should be a -# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen -# will append .docset to the name. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely -# identify the documentation publisher. This should be a reverse domain-name -# style string, e.g. com.mycompany.MyDocSet.documentation. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES, additional index files -# will be generated that can be used as input for tools like the -# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) -# of the generated HTML documentation. - -GENERATE_HTMLHELP = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can -# be used to specify the file name of the resulting .chm file. You -# can add a path in front of the file if the result should not be -# written to the html output directory. - -CHM_FILE = - -# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can -# be used to specify the location (absolute path including file name) of -# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run -# the HTML help compiler on the generated index.hhp. - -HHC_LOCATION = - -# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag -# controls if a separate .chi index file is generated (YES) or that -# it should be included in the master .chm file (NO). - -GENERATE_CHI = NO - -# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING -# is used to encode HtmlHelp index (hhk), content (hhc) and project file -# content. - -CHM_INDEX_ENCODING = - -# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag -# controls whether a binary table of contents is generated (YES) or a -# normal table of contents (NO) in the .chm file. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members -# to the contents of the HTML help documentation and to the tree view. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated -# that can be used as input for Qt's qhelpgenerator to generate a -# Qt Compressed Help (.qch) of the generated HTML documentation. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can -# be used to specify the file name of the resulting .qch file. -# The path specified is relative to the HTML output folder. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#namespace - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating -# Qt Help Project output. For more information please see -# http://doc.trolltech.com/qthelpproject.html#virtual-folders - -QHP_VIRTUAL_FOLDER = doc - -# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to -# add. For more information please see -# http://doc.trolltech.com/qthelpproject.html#custom-filters - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see -# -# Qt Help Project / Custom Filters. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's -# filter section matches. -# -# Qt Help Project / Filter Attributes. - -QHP_SECT_FILTER_ATTRS = - -# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can -# be used to specify the location of Qt's qhelpgenerator. -# If non-empty doxygen will try to run qhelpgenerator on the generated -# .qhp file. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files -# will be generated, which together with the HTML files, form an Eclipse help -# plugin. To install this plugin and make it available under the help contents -# menu in Eclipse, the contents of the directory containing the HTML and XML -# files needs to be copied into the plugins directory of eclipse. The name of -# the directory within the plugins directory should be the same as -# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before -# the help appears. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have -# this name. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) -# at top of each HTML page. The value NO (the default) enables the index and -# the value YES disables it. Since the tabs have the same information as the -# navigation tree you can set this option to NO if you already set -# GENERATE_TREEVIEW to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. -# If the tag value is set to YES, a side panel will be generated -# containing a tree-like index structure (just like the one that -# is generated for HTML Help). For this to work a browser that supports -# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). -# Windows users are probably better off using the HTML help feature. -# Since the tree basically has the same information as the tab index you -# could consider to set DISABLE_INDEX to NO when enabling this option. - -GENERATE_TREEVIEW = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values -# (range [0,1..20]) that doxygen will group on one line in the generated HTML -# documentation. Note that a value of 0 will completely suppress the enum -# values from appearing in the overview section. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be -# used to set the initial width (in pixels) of the frame in which the tree -# is shown. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open -# links to external symbols imported via tag files in a separate window. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of Latex formulas included -# as images in the HTML documentation. The default is 10. Note that -# when you change the font size after a successful doxygen run you need -# to manually remove any form_*.png images from the HTML output directory -# to force them to be regenerated. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are -# not supported properly for IE 6.0, but are supported on all modern browsers. -# Note that when changing this option you need to delete any form_*.png files -# in the HTML output before the changes have effect. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax -# (see http://www.mathjax.org) which uses client side Javascript for the -# rendering instead of using prerendered bitmaps. Use this if you do not -# have LaTeX installed or if you want to formulas look prettier in the HTML -# output. When enabled you may also need to install MathJax separately and -# configure the path to it using the MATHJAX_RELPATH option. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and -# SVG. The default value is HTML-CSS, which is slower, but has the best -# compatibility. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the -# HTML output directory using the MATHJAX_RELPATH option. The destination -# directory should contain the MathJax.js script. For instance, if the mathjax -# directory is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to -# the MathJax Content Delivery Network so you can quickly see the result without -# installing MathJax. -# However, it is strongly recommended to install a local -# copy of MathJax from http://www.mathjax.org before deployment. - -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest - -# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension -# names that should be enabled during MathJax rendering. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript -# pieces of code that will be used on startup of the MathJax code. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box -# for the HTML output. The underlying search engine uses javascript -# and DHTML and should work on any modern browser. Note that when using -# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets -# (GENERATE_DOCSET) there is already a search function so this one should -# typically be disabled. For large projects the javascript based search engine -# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. - -SEARCHENGINE = YES - -# When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. -# There are two flavours of web server based search depending on the -# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for -# searching and an index file used by the script. When EXTERNAL_SEARCH is -# enabled the indexing and searching needs to be provided by external tools. -# See the manual for details. - -SERVER_BASED_SEARCH = NO - -# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP -# script for searching. Instead the search results are written to an XML file -# which needs to be processed by an external indexer. Doxygen will invoke an -# external search engine pointed to by the SEARCHENGINE_URL option to obtain -# the search results. Doxygen ships with an example indexer (doxyindexer) and -# search engine (doxysearch.cgi) which are based on the open source search -# engine library Xapian. See the manual for configuration details. - -EXTERNAL_SEARCH = NO - -# The SEARCHENGINE_URL should point to a search engine hosted by a web server -# which will returned the search results when EXTERNAL_SEARCH is enabled. -# Doxygen ships with an example search engine (doxysearch) which is based on -# the open source search engine library Xapian. See the manual for configuration -# details. - -SEARCHENGINE_URL = - -# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed -# search data is written to a file for indexing by an external tool. With the -# SEARCHDATA_FILE tag the name of this file can be specified. - -SEARCHDATA_FILE = searchdata.xml - -# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the -# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is -# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple -# projects and redirect the results back to the right project. - -EXTERNAL_SEARCH_ID = - -# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen -# projects other than the one defined by this configuration file, but that are -# all added to the same external search index. Each project needs to have a -# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id -# of to a relative location where the documentation can be found. -# The format is: EXTRA_SEARCH_MAPPINGS = id1 = loc1 id2 = loc2 ... - -EXTRA_SEARCH_MAPPINGS = - -#--------------------------------------------------------------------------- -# configuration options related to the LaTeX output -#--------------------------------------------------------------------------- - -# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will -# generate Latex output. - -GENERATE_LATEX = NO - -# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `latex' will be used as the default path. - -LATEX_OUTPUT = latex - -# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be -# invoked. If left blank `latex' will be used as the default command name. -# Note that when enabling USE_PDFLATEX this option is only used for -# generating bitmaps for formulas in the HTML output, but not in the -# Makefile that is written to the output directory. - -LATEX_CMD_NAME = latex - -# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to -# generate index for LaTeX. If left blank `makeindex' will be used as the -# default command name. - -MAKEINDEX_CMD_NAME = makeindex - -# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact -# LaTeX documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_LATEX = NO - -# The PAPER_TYPE tag can be used to set the paper type that is used -# by the printer. Possible values are: a4, letter, legal and -# executive. If left blank a4 will be used. - -PAPER_TYPE = a4 - -# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX -# packages that should be included in the LaTeX output. - -EXTRA_PACKAGES = - -# The LATEX_HEADER tag can be used to specify a personal LaTeX header for -# the generated latex document. The header should contain everything until -# the first chapter. If it is left blank doxygen will generate a -# standard header. Notice: only use this tag if you know what you are doing! - -LATEX_HEADER = - -# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for -# the generated latex document. The footer should contain everything after -# the last chapter. If it is left blank doxygen will generate a -# standard footer. Notice: only use this tag if you know what you are doing! - -LATEX_FOOTER = - -# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images -# or other source files which should be copied to the LaTeX output directory. -# Note that the files will be copied as-is; there are no commands or markers -# available. - -LATEX_EXTRA_FILES = - -# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated -# is prepared for conversion to pdf (using ps2pdf). The pdf file will -# contain links (just like the HTML output) instead of page references -# This makes the output suitable for online browsing using a pdf viewer. - -PDF_HYPERLINKS = YES - -# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of -# plain latex in the generated Makefile. Set this option to YES to get a -# higher quality PDF documentation. - -USE_PDFLATEX = YES - -# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. -# command to the generated LaTeX files. This will instruct LaTeX to keep -# running if errors occur, instead of asking the user for help. -# This option is also used when generating formulas in HTML. - -LATEX_BATCHMODE = NO - -# If LATEX_HIDE_INDICES is set to YES then doxygen will not -# include the index chapters (such as File Index, Compound Index, etc.) -# in the output. - -LATEX_HIDE_INDICES = NO - -# If LATEX_SOURCE_CODE is set to YES then doxygen will include -# source code with syntax highlighting in the LaTeX output. -# Note that which sources are shown also depends on other settings -# such as SOURCE_BROWSER. - -LATEX_SOURCE_CODE = NO - -# The LATEX_BIB_STYLE tag can be used to specify the style to use for the -# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See -# http://en.wikipedia.org/wiki/BibTeX for more info. - -LATEX_BIB_STYLE = plain - -#--------------------------------------------------------------------------- -# configuration options related to the RTF output -#--------------------------------------------------------------------------- - -# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output -# The RTF output is optimized for Word 97 and may not look very pretty with -# other RTF readers or editors. - -GENERATE_RTF = NO - -# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `rtf' will be used as the default path. - -RTF_OUTPUT = rtf - -# If the COMPACT_RTF tag is set to YES Doxygen generates more compact -# RTF documents. This may be useful for small projects and may help to -# save some trees in general. - -COMPACT_RTF = NO - -# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated -# will contain hyperlink fields. The RTF file will -# contain links (just like the HTML output) instead of page references. -# This makes the output suitable for online browsing using WORD or other -# programs which support those fields. -# Note: wordpad (write) and others do not support links. - -RTF_HYPERLINKS = NO - -# Load style sheet definitions from file. Syntax is similar to doxygen's -# config file, i.e. a series of assignments. You only have to provide -# replacements, missing definitions are set to their default value. - -RTF_STYLESHEET_FILE = - -# Set optional variables used in the generation of an rtf document. -# Syntax is similar to doxygen's config file. - -RTF_EXTENSIONS_FILE = - -#--------------------------------------------------------------------------- -# configuration options related to the man page output -#--------------------------------------------------------------------------- - -# If the GENERATE_MAN tag is set to YES (the default) Doxygen will -# generate man pages - -GENERATE_MAN = NO - -# The MAN_OUTPUT tag is used to specify where the man pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `man' will be used as the default path. - -MAN_OUTPUT = man - -# The MAN_EXTENSION tag determines the extension that is added to -# the generated man pages (default is the subroutine's section .3) - -MAN_EXTENSION = .3 - -# If the MAN_LINKS tag is set to YES and Doxygen generates man output, -# then it will generate one additional man file for each entity -# documented in the real man page(s). These additional files -# only source the real man page, but without them the man command -# would be unable to find the correct page. The default is NO. - -MAN_LINKS = NO - -#--------------------------------------------------------------------------- -# configuration options related to the XML output -#--------------------------------------------------------------------------- - -# If the GENERATE_XML tag is set to YES Doxygen will -# generate an XML file that captures the structure of -# the code including all documentation. - -GENERATE_XML = NO - -# The XML_OUTPUT tag is used to specify where the XML pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be -# put in front of it. If left blank `xml' will be used as the default path. - -XML_OUTPUT = xml - -# The XML_SCHEMA tag can be used to specify an XML schema, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_SCHEMA = - -# The XML_DTD tag can be used to specify an XML DTD, -# which can be used by a validating XML parser to check the -# syntax of the XML files. - -XML_DTD = - -# If the XML_PROGRAMLISTING tag is set to YES Doxygen will -# dump the program listings (including syntax highlighting -# and cross-referencing information) to the XML output. Note that -# enabling this will significantly increase the size of the XML output. - -XML_PROGRAMLISTING = YES - -#--------------------------------------------------------------------------- -# configuration options related to the DOCBOOK output -#--------------------------------------------------------------------------- - -# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files -# that can be used to generate PDF. - -GENERATE_DOCBOOK = NO - -# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. -# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in -# front of it. If left blank docbook will be used as the default path. - -DOCBOOK_OUTPUT = docbook - -#--------------------------------------------------------------------------- -# configuration options for the AutoGen Definitions output -#--------------------------------------------------------------------------- - -# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will -# generate an AutoGen Definitions (see autogen.sf.net) file -# that captures the structure of the code including all -# documentation. Note that this feature is still experimental -# and incomplete at the moment. - -GENERATE_AUTOGEN_DEF = NO - -#--------------------------------------------------------------------------- -# configuration options related to the Perl module output -#--------------------------------------------------------------------------- - -# If the GENERATE_PERLMOD tag is set to YES Doxygen will -# generate a Perl module file that captures the structure of -# the code including all documentation. Note that this -# feature is still experimental and incomplete at the -# moment. - -GENERATE_PERLMOD = NO - -# If the PERLMOD_LATEX tag is set to YES Doxygen will generate -# the necessary Makefile rules, Perl scripts and LaTeX code to be able -# to generate PDF and DVI output from the Perl module output. - -PERLMOD_LATEX = NO - -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be -# nicely formatted so it can be parsed by a human reader. -# This is useful -# if you want to understand what is going on. -# On the other hand, if this -# tag is set to NO the size of the Perl module output will be much smaller -# and Perl will parse it just the same. - -PERLMOD_PRETTY = YES - -# The names of the make variables in the generated doxyrules.make file -# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. -# This is useful so different doxyrules.make files included by the same -# Makefile don't overwrite each other's variables. - -PERLMOD_MAKEVAR_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the preprocessor -#--------------------------------------------------------------------------- - -# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will -# evaluate all C-preprocessor directives found in the sources and include -# files. - -ENABLE_PREPROCESSING = YES - -# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro -# names in the source code. If set to NO (the default) only conditional -# compilation will be performed. Macro expansion can be done in a controlled -# way by setting EXPAND_ONLY_PREDEF to YES. - -MACRO_EXPANSION = NO - -# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES -# then the macro expansion is limited to the macros specified with the -# PREDEFINED and EXPAND_AS_DEFINED tags. - -EXPAND_ONLY_PREDEF = NO - -# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files -# pointed to by INCLUDE_PATH will be searched when a #include is found. - -SEARCH_INCLUDES = YES - -# The INCLUDE_PATH tag can be used to specify one or more directories that -# contain include files that are not input files but should be processed by -# the preprocessor. - -INCLUDE_PATH = - -# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard -# patterns (like *.h and *.hpp) to filter out the header-files in the -# directories. If left blank, the patterns specified with FILE_PATTERNS will -# be used. - -INCLUDE_FILE_PATTERNS = - -# The PREDEFINED tag can be used to specify one or more macro names that -# are defined before the preprocessor is started (similar to the -D option of -# gcc). The argument of the tag is a list of macros of the form: name -# or name = definition (no spaces). If the definition and the = are -# omitted = 1 is assumed. To prevent a macro definition from being -# undefined via #undef or recursively expanded use the : = operator -# instead of the = operator. - -PREDEFINED = - -# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then -# this tag can be used to specify a list of macro names that should be expanded. -# The macro definition that is found in the sources will be used. -# Use the PREDEFINED tag if you want to use a different macro definition that -# overrules the definition found in the source code. - -EXPAND_AS_DEFINED = - -# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then -# doxygen's preprocessor will remove all references to function-like macros -# that are alone on a line, have an all uppercase name, and do not end with a -# semicolon, because these will confuse the parser if not removed. - -SKIP_FUNCTION_MACROS = YES - -#--------------------------------------------------------------------------- -# Configuration::additions related to external references -#--------------------------------------------------------------------------- - -# The TAGFILES option can be used to specify one or more tagfiles. For each -# tag file the location of the external documentation should be added. The -# format of a tag file without this location is as follows: -# -# TAGFILES = file1 file2 ... -# Adding location for the tag files is done as follows: -# -# TAGFILES = file1 = loc1 "file2 = loc2" ... -# where "loc1" and "loc2" can be relative or absolute paths -# or URLs. Note that each tag file must have a unique name (where the name does -# NOT include the path). If a tag file is not located in the directory in which -# doxygen is run, you must also specify the path to the tagfile here. - -TAGFILES = - -# When a file name is specified after GENERATE_TAGFILE, doxygen will create -# a tag file that is based on the input files it reads. - -GENERATE_TAGFILE = - -# If the ALLEXTERNALS tag is set to YES all external classes will be listed -# in the class index. If set to NO only the inherited external classes -# will be listed. - -ALLEXTERNALS = NO - -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed -# in the modules index. If set to NO, only the current project's groups will -# be listed. - -EXTERNAL_GROUPS = YES - -# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed -# in the related pages index. If set to NO, only the current project's -# pages will be listed. - -EXTERNAL_PAGES = YES - -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of `which perl'). - -PERL_PATH = /usr/bin/perl - -#--------------------------------------------------------------------------- -# Configuration options related to the dot tool -#--------------------------------------------------------------------------- - -# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will -# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base -# or super classes. Setting the tag to NO turns the diagrams off. Note that -# this option also works with HAVE_DOT disabled, but it is recommended to -# install and use dot, since it yields more powerful graphs. - -CLASS_DIAGRAMS = YES - -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see -# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - -# If set to YES, the inheritance and collaboration graphs will hide -# inheritance and usage relations if the target is undocumented -# or is not a class. - -HIDE_UNDOC_RELATIONS = YES - -# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is -# available from the path. This tool is part of Graphviz, a graph visualization -# toolkit from AT&T and Lucent Bell Labs. The other options in this section -# have no effect if this option is set to NO (the default) - -HAVE_DOT = NO - -# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is -# allowed to run in parallel. When set to 0 (the default) doxygen will -# base this on the number of processors available in the system. You can set it -# explicitly to a value larger than 0 to get control over the balance -# between CPU load and processing speed. - -DOT_NUM_THREADS = 0 - -# By default doxygen will use the Helvetica font for all dot files that -# doxygen generates. When you want a differently looking font you can specify -# the font name using DOT_FONTNAME. You need to make sure dot is able to find -# the font, which can be done by putting it in a standard location or by setting -# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the -# directory containing the font. - -DOT_FONTNAME = Helvetica - -# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. -# The default size is 10pt. - -DOT_FONTSIZE = 10 - -# By default doxygen will tell dot to use the Helvetica font. -# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to -# set the path where dot can find it. - -DOT_FONTPATH = - -# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect inheritance relations. Setting this tag to YES will force the -# CLASS_DIAGRAMS tag to NO. - -CLASS_GRAPH = YES - -# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for each documented class showing the direct and -# indirect implementation dependencies (inheritance, containment, and -# class references variables) of the class with other documented classes. - -COLLABORATION_GRAPH = YES - -# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen -# will generate a graph for groups, showing the direct groups dependencies - -GROUP_GRAPHS = YES - -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and -# collaboration diagrams in a style similar to the OMG's Unified Modeling -# Language. - -UML_LOOK = NO - -# If the UML_LOOK tag is enabled, the fields and methods are shown inside -# the class node. If there are many fields or methods and many nodes the -# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS -# threshold limits the number of items for each type to make the size more -# manageable. Set this to 0 for no limit. Note that the threshold may be -# exceeded by 50% before the limit is enforced. - -UML_LIMIT_NUM_FIELDS = 10 - -# If set to YES, the inheritance and collaboration graphs will show the -# relations between templates and their instances. - -TEMPLATE_RELATIONS = NO - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT -# tags are set to YES then doxygen will generate a graph for each documented -# file showing the direct and indirect include dependencies of the file with -# other documented files. - -INCLUDE_GRAPH = YES - -# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and -# HAVE_DOT tags are set to YES then doxygen will generate a graph for each -# documented header file showing the documented files that directly or -# indirectly include this file. - -INCLUDED_BY_GRAPH = YES - -# If the CALL_GRAPH and HAVE_DOT options are set to YES then -# doxygen will generate a call dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable call graphs -# for selected functions only using the \callgraph command. - -CALL_GRAPH = NO - -# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then -# doxygen will generate a caller dependency graph for every global function -# or class method. Note that enabling this option will significantly increase -# the time of a run. So in most cases it will be better to enable caller -# graphs for selected functions only using the \callergraph command. - -CALLER_GRAPH = NO - -# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen -# will generate a graphical hierarchy of all classes instead of a textual one. - -GRAPHICAL_HIERARCHY = YES - -# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES -# then doxygen will show the dependencies a directory has on other directories -# in a graphical way. The dependency relations are determined by the #include -# relations between the files in the directories. - -DIRECTORY_GRAPH = YES - -# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. Possible values are svg, png, jpg, or gif. -# If left blank png will be used. If you choose svg you need to set -# HTML_FILE_EXTENSION to xhtml in order to make the SVG files -# visible in IE 9+ (other browsers do not have this requirement). - -DOT_IMAGE_FORMAT = png - -# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to -# enable generation of interactive SVG images that allow zooming and panning. -# Note that this requires a modern browser other than Internet Explorer. -# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you -# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files -# visible. Older versions of IE do not have SVG support. - -INTERACTIVE_SVG = NO - -# The tag DOT_PATH can be used to specify the path where the dot tool can be -# found. If left blank, it is assumed the dot tool can be found in the path. - -DOT_PATH = - -# The DOTFILE_DIRS tag can be used to specify one or more directories that -# contain dot files that are included in the documentation (see the -# \dotfile command). - -DOTFILE_DIRS = - -# The MSCFILE_DIRS tag can be used to specify one or more directories that -# contain msc files that are included in the documentation (see the -# \mscfile command). - -MSCFILE_DIRS = - -# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of -# nodes that will be shown in the graph. If the number of nodes in a graph -# becomes larger than this value, doxygen will truncate the graph, which is -# visualized by representing a node as a red box. Note that doxygen if the -# number of direct children of the root node in a graph is already larger than -# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note -# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. - -DOT_GRAPH_MAX_NODES = 50 - -# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the -# graphs generated by dot. A depth value of 3 means that only nodes reachable -# from the root by following a path via at most 3 edges will be shown. Nodes -# that lay further from the root node will be omitted. Note that setting this -# option to 1 or 2 may greatly reduce the computation time needed for large -# code bases. Also note that the size of a graph can be further restricted by -# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. - -MAX_DOT_GRAPH_DEPTH = 0 - -# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent -# background. This is disabled by default, because dot on Windows does not -# seem to support this out of the box. Warning: Depending on the platform used, -# enabling this option may lead to badly anti-aliased labels on the edges of -# a graph (i.e. they become hard to read). - -DOT_TRANSPARENT = NO - -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output -# files in one run (i.e. multiple -o and -T options on the command line). This -# makes dot run faster, but since only newer versions of dot (>1.8.10) -# support this, this feature is disabled by default. - -DOT_MULTI_TARGETS = YES - -# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will -# generate a legend page explaining the meaning of the various boxes and -# arrows in the dot generated graphs. - -GENERATE_LEGEND = YES - -# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will -# remove the intermediate dot files that are used to generate -# the various graphs. - -DOT_CLEANUP = YES diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 20fb9c7..0000000 --- a/LICENSE +++ /dev/null @@ -1,458 +0,0 @@ - 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 diff --git a/README.md b/README.md deleted file mode 100644 index d3e3ade..0000000 --- a/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# libfm-qt - -## Overview - -libfm-qt is the Qt port of libfm, a library providing components to build -desktop file managers which belongs to [LXDE](https://lxde.org). - -libfm-qt is licensed under the terms of the -[LGPLv2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) -or any later version. See file LICENSE for its full text. - -## 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/lxqt/lxqt-build-tools) and optionally Git -to pull latest VCS checkouts. The localization files were outsourced to -repository [lxqt-l10n](https://github.com/lxqt/lxqt-l10n) so the corresponding -dependencies are needed, too. Please refer to this repository's `README.md` for -further information. - -Code configuration is handled by CMake. CMake variable `CMAKE_INSTALL_PREFIX` -has to be set to `/usr` on most operating systems, depending on the way library -paths are dealt with on 64bit systems variables like `CMAKE_INSTALL_LIBDIR` may -have to be set as well. - -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/lxqt/pcmanfm-qt/issues. diff --git a/cmake/fm-qt-config.cmake.in b/cmake/fm-qt-config.cmake.in deleted file mode 100644 index a44783f..0000000 --- a/cmake/fm-qt-config.cmake.in +++ /dev/null @@ -1,41 +0,0 @@ -#============================================================================= -# Copyright 2015 Luís Pereira -# -# 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() diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt deleted file mode 100644 index 9e9cd2c..0000000 --- a/data/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -install(FILES - "archivers.list" - "terminals.list" - DESTINATION "${CMAKE_INSTALL_DATADIR}/libfm-qt" -) - -install(FILES - "libfm-qt-mimetypes.xml" - DESTINATION "${CMAKE_INSTALL_DATADIR}/mime/packages" -) diff --git a/data/archivers.list b/data/archivers.list deleted file mode 100644 index 317953a..0000000 --- a/data/archivers.list +++ /dev/null @@ -1,35 +0,0 @@ -[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 diff --git a/data/libfm-qt-mimetypes.xml b/data/libfm-qt-mimetypes.xml deleted file mode 100644 index 7f509a9..0000000 --- a/data/libfm-qt-mimetypes.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - Windows installer - Windows 安裝程式 - - - - - MS VBScript - - - - - C# source - C# 程式碼 - - - - - 應用程式捷徑 - - - - - - - - - - - - - - diff --git a/data/terminals.list b/data/terminals.list deleted file mode 100644 index 3c49a10..0000000 --- a/data/terminals.list +++ /dev/null @@ -1,80 +0,0 @@ -[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 - -[kitty] -desktop_id=kitty.desktop diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 0695854..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,269 +0,0 @@ -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/filetransferjob.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/basicfilelauncher.cpp - core/volumemanager.cpp - core/userinfocache.cpp - core/thumbnailer.cpp - core/terminal.cpp - core/archiver.cpp - core/templates.cpp - # custom actions - customactions/fileaction.cpp - customactions/fileactionprofile.cpp - customactions/fileactioncondition.cpp -) - -set(libfm_SRCS - ${libfm_core_SRCS} - libfmqt.cpp - bookmarkaction.cpp - sidepane.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 -) - -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} - ${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 - "$" - "$" -) - -target_compile_definitions(${LIBFM_QT_LIBRARY_NAME} - PRIVATE "LIBFM_QT_DATA_DIR=\"${LIBFM_QT_DATA_DIR}\"" - "QT_NO_FOREACH" - 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 -# https://www.freebsd.org/doc/handbook/dirstructure.html -if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") - install(FILES - "${CMAKE_CURRENT_BINARY_DIR}/lib${LIBFM_QT_LIBRARY_NAME}.pc" - 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}) - diff --git a/src/app-chooser-dialog.ui b/src/app-chooser-dialog.ui deleted file mode 100644 index ef1ebee..0000000 --- a/src/app-chooser-dialog.ui +++ /dev/null @@ -1,183 +0,0 @@ - - - AppChooserDialog - - - - 0 - 0 - 432 - 387 - - - - Choose an Application - - - - QFormLayout::AllNonFixedFieldsGrow - - - - - - - - - 0 - 1 - - - - 0 - - - - Installed Applications - - - - - - - - - - Custom Command - - - - - - Command line to execute: - - - - - - - - - - Application name: - - - - - - - - - - <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> - - - Qt::RichText - - - - - - - false - - - Keep terminal window open after command execution - - - - - - - Execute in terminal emulator - - - - - - - - - - - Set selected application as default action of this file type - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - Fm::AppMenuView - QTreeView -
appmenuview.h
-
-
- - - - buttonBox - accepted() - AppChooserDialog - accept() - - - 227 - 359 - - - 157 - 274 - - - - - buttonBox - rejected() - AppChooserDialog - reject() - - - 295 - 365 - - - 286 - 274 - - - - - useTerminal - toggled(bool) - keepTermOpen - setEnabled(bool) - - - 72 - 260 - - - 79 - 282 - - - - -
diff --git a/src/appchoosercombobox.cpp b/src/appchoosercombobox.cpp deleted file mode 100644 index 24efa31..0000000 --- a/src/appchoosercombobox.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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 "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: https://forum.qt.io/topic/20998/qt5-new-signals-slots-syntax-does-not-work-solved - connect((QComboBox*)this, static_cast(&QComboBox::currentIndexChanged), this, &AppChooserComboBox::onCurrentIndexChanged); -} - -AppChooserComboBox::~AppChooserComboBox() { -} - -void AppChooserComboBox::setMimeType(std::shared_ptr 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 { - // the elements of appInfos_ and the combo indexes before "Customize" - // always have a one-to-one correspondence - int idx = currentIndex(); - return idx >= 0 && !appInfos_.empty() ? 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 diff --git a/src/appchoosercombobox.h b/src/appchoosercombobox.h deleted file mode 100644 index 03149e0..0000000 --- a/src/appchoosercombobox.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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 -#include - -#include - -#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 mimeType); - - const std::shared_ptr& 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 mimeType_; - std::vector 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 diff --git a/src/appchooserdialog.cpp b/src/appchooserdialog.cpp deleted file mode 100644 index 0b508a2..0000000 --- a/src/appchooserdialog.cpp +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright 2010-2014 Hong Jen Yee (PCMan) - * Copyright 2012-2013 Andriy Grytsenko (LStranger) - * - * 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 -#include - -namespace Fm { - -AppChooserDialog::AppChooserDialog(std::shared_ptr 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 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 diff --git a/src/appchooserdialog.h b/src/appchooserdialog.h deleted file mode 100644 index ef36dfc..0000000 --- a/src/appchooserdialog.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2010-2014 Hong Jen Yee (PCMan) - * Copyright 2012-2013 Andriy Grytsenko (LStranger) - * - * 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 -#include "libfmqtglobals.h" -#include - -#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 mimeType, QWidget* parent = nullptr, Qt::WindowFlags f = 0); - ~AppChooserDialog(); - - virtual void accept(); - - void setMimeType(std::shared_ptr mimeType); - - const std::shared_ptr& 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 mimeType_; - bool canSetDefault_; - Fm::GAppInfoPtr selectedApp_; -}; - -} - -#endif // FM_APPCHOOSERDIALOG_H diff --git a/src/applaunchcontext.cpp b/src/applaunchcontext.cpp deleted file mode 100644 index e599485..0000000 --- a/src/applaunchcontext.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 -#include - -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; -} diff --git a/src/applaunchcontext.h b/src/applaunchcontext.h deleted file mode 100644 index 606c3b6..0000000 --- a/src/applaunchcontext.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 -#include - -#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 diff --git a/src/appmenuview.cpp b/src/appmenuview.cpp deleted file mode 100644 index 615b971..0000000 --- a/src/appmenuview.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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 -#include "appmenuview_p.h" -#include - -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(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 diff --git a/src/appmenuview.h b/src/appmenuview.h deleted file mode 100644 index 12d42a7..0000000 --- a/src/appmenuview.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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 -#include "libfmqtglobals.h" -#include -#include - -#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(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 diff --git a/src/appmenuview_p.h b/src/appmenuview_p.h deleted file mode 100644 index 7dd3d53..0000000 --- a/src/appmenuview_p.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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 -#include -#include "core/iconinfo.h" - -namespace Fm { - -class AppMenuViewItem : public QStandardItem { -public: - explicit AppMenuViewItem(MenuCacheItem* item): - item_(menu_cache_item_ref(item)) { - std::shared_ptr 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 diff --git a/src/bookmarkaction.cpp b/src/bookmarkaction.cpp deleted file mode 100644 index 502126a..0000000 --- a/src/bookmarkaction.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 item, QObject* parent): - QAction(parent), - item_(std::move(item)) { - - setText(item_->name()); -} - -} // namespace Fm diff --git a/src/bookmarkaction.h b/src/bookmarkaction.h deleted file mode 100644 index 08fc73f..0000000 --- a/src/bookmarkaction.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 -#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 item, QObject* parent = 0); - - const std::shared_ptr& bookmark() const { - return item_; - } - - const Fm::FilePath& path() const { - return item_->path(); - } - -private: - std::shared_ptr item_; -}; - -} - -#endif // BOOKMARKACTION_H diff --git a/src/browsehistory.cpp b/src/browsehistory.cpp deleted file mode 100644 index 54671fb..0000000 --- a/src/browsehistory.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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(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(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(currentIndex_) + 1 < items_.size()); -} - -int BrowseHistory::forward() { - if(canForward()) { - ++currentIndex_; - } - return currentIndex_; -} - -void BrowseHistory::setMaxCount(int maxCount) { - maxCount_ = maxCount; - if(items_.size() > static_cast(maxCount)) { - // TODO: remove some items - } -} - - -} // namespace Fm diff --git a/src/browsehistory.h b/src/browsehistory.h deleted file mode 100644 index 6ea570e..0000000 --- a/src/browsehistory.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 -#include - -#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 items_; - int currentIndex_; - int maxCount_; -}; - -} - -#endif // FM_BROWSEHISTORY_H diff --git a/src/cachedfoldermodel.cpp b/src/cachedfoldermodel.cpp deleted file mode 100644 index defa398..0000000 --- a/src/cachedfoldermodel.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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& folder): - FolderModel(), - refCount(1) { - FolderModel::setFolder(folder); -} - -CachedFolderModel::~CachedFolderModel() { - // qDebug("delete CachedFolderModel"); -} - -CachedFolderModel* CachedFolderModel::modelFromFolder(const std::shared_ptr& folder) { - QVariant cache = folder->property(cacheKey); - CachedFolderModel* model = cache.value(); - 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()); - delete(this); - } -} - - -} // namespace Fm diff --git a/src/cachedfoldermodel.h b/src/cachedfoldermodel.h deleted file mode 100644 index 1c22489..0000000 --- a/src/cachedfoldermodel.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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& folder); - void ref() { - ++refCount; - } - void unref(); - - static CachedFolderModel* modelFromFolder(const std::shared_ptr& 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 diff --git a/src/colorbutton.cpp b/src/colorbutton.cpp deleted file mode 100644 index ac95b45..0000000 --- a/src/colorbutton.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 - -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 diff --git a/src/colorbutton.h b/src/colorbutton.h deleted file mode 100644 index d5fa89d..0000000 --- a/src/colorbutton.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 -#include - -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 diff --git a/src/core/archiver.cpp b/src/core/archiver.cpp deleted file mode 100644 index b38990e..0000000 --- a/src/core/archiver.cpp +++ /dev/null @@ -1,174 +0,0 @@ -#include "libfmqtglobals.h" -#include "archiver.h" - -#include -#include -#include - -#include - -namespace Fm { - -Archiver* Archiver::defaultArchiver_ = nullptr; // static -std::vector> Archiver::allArchivers_; // static - -Archiver::Archiver() { -} - -bool Archiver::isMimeTypeSupported(const char* type) { - char** p; - if(G_UNLIKELY(!type)) { - return false; - } - for(p = mimeTypes_.get(); *p; ++p) { - if(strcmp(*p, type) == 0) { - return true; - } - } - return false; -} - -bool Archiver::launchProgram(GAppLaunchContext* ctx, const char* cmd, const FilePathList& files, const FilePath& dir) { - char* _cmd = NULL; - const char* dir_place_holder; - GKeyFile* dummy; - - if(dir.isValid() && (dir_place_holder = strstr(cmd, "%d"))) { - CStrPtr dir_str; - int len; - if(strstr(cmd, "%U") || strstr(cmd, "%u")) { /* supports URI */ - dir_str = dir.uri(); - } - else { - dir_str = dir.localPath(); - } - - // FIXME: remove libfm dependency here - /* replace all % with %% so encoded URI can be handled correctly when parsing Exec key. */ - std::string percentEscapedDir; - for(auto p = dir_str.get(); *p; ++p) { - percentEscapedDir += *p; - if(*p == '%') { - percentEscapedDir += '%'; - } - } - - /* quote the path or URI */ - dir_str = CStrPtr{g_shell_quote(percentEscapedDir.c_str())}; - - len = strlen(cmd) - 2 + strlen(dir_str.get()) + 1; - _cmd = (char*)g_malloc(len); - len = (dir_place_holder - cmd); - strncpy(_cmd, cmd, len); - strcpy(_cmd + len, dir_str.get()); - strcat(_cmd, dir_place_holder + 2); - cmd = _cmd; - } - - /* create a fake key file to cheat GDesktopAppInfo */ - dummy = g_key_file_new(); - g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Type", "Application"); - g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Name", program_.get()); - - /* replace all % with %% so encoded URI can be handled correctly when parsing Exec key. */ - g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Exec", cmd); - GAppInfoPtr app{reinterpret_cast(g_desktop_app_info_new_from_keyfile(dummy)), false}; - - g_key_file_free(dummy); - g_debug("cmd = %s", cmd); - if(app) { - GList* uris = NULL; - for(auto& file: files) { - uris = g_list_prepend(uris, g_strdup(file.uri().get())); - } - g_app_info_launch_uris(app.get(), uris, ctx, NULL); - g_list_foreach(uris, (GFunc)g_free, NULL); - g_list_free(uris); - } - g_free(_cmd); - return true; -} - -bool Archiver::createArchive(GAppLaunchContext* ctx, const FilePathList& files) { - if(createCmd_ && !files.empty()) { - launchProgram(ctx, createCmd_.get(), files, FilePath{}); - } - return false; -} - -bool Archiver::extractArchives(GAppLaunchContext* ctx, const FilePathList& files) { - if(extractCmd_ && !files.empty()) { - launchProgram(ctx, extractCmd_.get(), files, FilePath{}); - } - return false; -} - -bool Archiver::extractArchivesTo(GAppLaunchContext* ctx, const FilePathList& files, const FilePath& dest_dir) { - if(extractToCmd_ && !files.empty()) { - launchProgram(ctx, extractToCmd_.get(), files, dest_dir); - } - return false; -} - -// static -Archiver* Archiver::defaultArchiver() { - allArchivers(); // to have a preliminary default archiver - return defaultArchiver_; -} - -void Archiver::setDefaultArchiverByName(const char *name) { - if(name) { - auto& all = allArchivers(); - for(auto& archiver: all) { - if(archiver->program_ && strcmp(archiver->program_.get(), name) == 0) { - defaultArchiver_ = archiver.get(); - break; - } - } - } -} - -// static -void Archiver::setDefaultArchiver(Archiver* archiver) { - if(archiver) { - defaultArchiver_ = archiver; - } -} - -// static -const std::vector >& Archiver::allArchivers() { - // load all archivers on demand - if(allArchivers_.empty()) { - GKeyFile* kf = g_key_file_new(); - if(g_key_file_load_from_file(kf, LIBFM_QT_DATA_DIR "/archivers.list", G_KEY_FILE_NONE, NULL)) { - gsize n_archivers; - CStrArrayPtr programs{g_key_file_get_groups(kf, &n_archivers)}; - if(programs) { - gsize i; - for(i = 0; i < n_archivers; ++i) { - auto program = programs[i]; - std::unique_ptr archiver{new Archiver{}}; - archiver->createCmd_ = CStrPtr{g_key_file_get_string(kf, program, "create", NULL)}; - archiver->extractCmd_ = CStrPtr{g_key_file_get_string(kf, program, "extract", NULL)}; - archiver->extractToCmd_ = CStrPtr{g_key_file_get_string(kf, program, "extract_to", NULL)}; - archiver->mimeTypes_ = CStrArrayPtr{g_key_file_get_string_list(kf, program, "mime_types", NULL, NULL)}; - archiver->program_ = CStrPtr{g_strdup(program)}; - - // if default archiver is not set, find the first program existing in the current system. - if(!defaultArchiver_) { - CStrPtr fullPath{g_find_program_in_path(program)}; - if(fullPath) { - defaultArchiver_ = archiver.get(); - } - } - - allArchivers_.emplace_back(std::move(archiver)); - } - } - } - g_key_file_free(kf); - } - return allArchivers_; -} - -} // namespace Fm diff --git a/src/core/archiver.h b/src/core/archiver.h deleted file mode 100644 index ba5368d..0000000 --- a/src/core/archiver.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef ARCHIVER_H -#define ARCHIVER_H - -#include "../libfmqtglobals.h" -#include "filepath.h" -#include "gioptrs.h" - -#include -#include - -namespace Fm { - -class LIBFM_QT_API Archiver { -public: - Archiver(); - - bool isMimeTypeSupported(const char* type); - - bool canCreateArchive() const { - return createCmd_ != nullptr; - } - - bool createArchive(GAppLaunchContext* ctx, const FilePathList& files); - - bool canExtractArchives() const { - return extractCmd_ != nullptr; - } - - bool extractArchives(GAppLaunchContext* ctx, const FilePathList& files); - - bool canExtractArchivesTo() const { - return extractToCmd_ != nullptr; - } - - bool extractArchivesTo(GAppLaunchContext* ctx, const FilePathList& files, const FilePath& dest_dir); - - /* get default GUI archivers used by libfm */ - static Archiver* defaultArchiver(); - - /* set default GUI archivers used by libfm */ - static void setDefaultArchiverByName(const char* name); - - /* set default GUI archivers used by libfm */ - static void setDefaultArchiver(Archiver* archiver); - - /* get a list of FmArchiver* of all GUI archivers known to libfm */ - static const std::vector>& allArchivers(); - - const char* program() const { - return program_.get(); - } - -private: - bool launchProgram(GAppLaunchContext* ctx, const char* cmd, const FilePathList& files, const FilePath &dir); - -private: - CStrPtr program_; - CStrPtr createCmd_; - CStrPtr extractCmd_; - CStrPtr extractToCmd_; - CStrArrayPtr mimeTypes_; - - static Archiver* defaultArchiver_; - static std::vector> allArchivers_; -}; - -} // namespace Fm - -#endif // ARCHIVER_H diff --git a/src/core/basicfilelauncher.cpp b/src/core/basicfilelauncher.cpp deleted file mode 100644 index 2012e09..0000000 --- a/src/core/basicfilelauncher.cpp +++ /dev/null @@ -1,345 +0,0 @@ -#include "basicfilelauncher.h" -#include "fileinfojob.h" -#include "mountoperation.h" - -#include -#include - -#include -#include - -#include -#include -#include - -namespace Fm { - -BasicFileLauncher::BasicFileLauncher(): - quickExec_{false} { -} - -BasicFileLauncher::~BasicFileLauncher() { -} - -bool BasicFileLauncher::launchFiles(const FileInfoList& fileInfos, GAppLaunchContext* ctx) { - std::unordered_map mimeTypeToFiles; - FileInfoList folderInfos; - FilePathList pathsToLaunch; - // classify files according to different mimetypes - for(auto& fileInfo : fileInfos) { - // qDebug("path: %s, target: %s", fileInfo->path().toString().get(), fileInfo->target().c_str()); - if(fileInfo->isDir()) { - folderInfos.emplace_back(fileInfo); - } - else if(fileInfo->isMountable()) { - if(fileInfo->target().empty()) { - // the mountable is not yet mounted so we have no target URI. - GErrorPtr err{G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED, - QObject::tr("The path is not mounted.")}; - if(!showError(ctx, err, fileInfo->path(), fileInfo)) { - // the user fail to handle the error, skip this file. - continue; - } - - // we do not have the target path in the FileInfo object. - // try to launch our path again to query the new file info later so we can get the mounted target URI. - pathsToLaunch.emplace_back(fileInfo->path()); - } - else { - // we have the target path, launch it later - pathsToLaunch.emplace_back(FilePath::fromPathStr(fileInfo->target().c_str())); - } - } - else if(fileInfo->isDesktopEntry()) { - // launch the desktop entry - launchDesktopEntry(fileInfo, FilePathList{}, ctx); - } - else if(fileInfo->isExecutableType()) { - // directly execute the file - launchExecutable(fileInfo, ctx); - } - else if(fileInfo->isShortcut()) { - // for shortcuts, launch their targets instead - auto path = handleShortcut(fileInfo, ctx); - if(path.isValid()) { - pathsToLaunch.emplace_back(path); - } - } - else { - auto& mimeType = fileInfo->mimeType(); - mimeTypeToFiles[mimeType->name()].emplace_back(fileInfo); - } - } - - // open folders - if(!folderInfos.empty()) { - GErrorPtr err; - openFolder(ctx, folderInfos, err); - } - - // open files of different mime-types with their default app - for(auto& typeFiles : mimeTypeToFiles) { - auto& mimeType = typeFiles.first; - auto& files = typeFiles.second; - GErrorPtr err; - GAppInfoPtr app{g_app_info_get_default_for_type(mimeType.c_str(), false), false}; - if(!app) { - app = chooseApp(files, mimeType.c_str(), err); - } - if(app) { - launchWithApp(app.get(), files.paths(), ctx); - } - } - - if(!pathsToLaunch.empty()) { - launchPaths(pathsToLaunch, ctx); - } - - return true; -} - -bool BasicFileLauncher::launchPaths(FilePathList paths, GAppLaunchContext* ctx) { - // FIXME: blocking with an event loop is not a good design :-( - QEventLoop eventLoop; - - auto job = new FileInfoJob{paths}; - job->setAutoDelete(false); // do not automatically delete the job since we want its results later. - - GObjectPtr ctxPtr{ctx}; - QObject::connect(job, &FileInfoJob::finished, - [&eventLoop]() { - // exit the event loop when the job is done - eventLoop.exit(); - }); - // run the job in another thread to not block the UI - job->runAsync(); - - // blocking until the job is done with a event loop - eventLoop.exec(); - - // launch the file info - launchFiles(job->files(), ctx); - - delete job; - return false; -} - -GAppInfoPtr BasicFileLauncher::chooseApp(const FileInfoList& /* fileInfos */, const char* /*mimeType*/, GErrorPtr& /* err */) { - return GAppInfoPtr{}; -} - -bool BasicFileLauncher::openFolder(GAppLaunchContext* ctx, const FileInfoList& folderInfos, GErrorPtr& err) { - auto app = chooseApp(folderInfos, "inode/directory", err); - if(app) { - launchWithApp(app.get(), folderInfos.paths(), ctx); - } - else { - showError(ctx, err); - } - return false; -} - -BasicFileLauncher::ExecAction BasicFileLauncher::askExecFile(const FileInfoPtr & /* file */) { - return ExecAction::DIRECT_EXEC; -} - -bool BasicFileLauncher::showError(GAppLaunchContext* /* ctx */, GErrorPtr& /* err */, const FilePath& /* path */, const FileInfoPtr& /* info */) { - return false; -} - -int BasicFileLauncher::ask(const char* /* msg */, char* const* /* btn_labels */, int default_btn) { - return default_btn; -} - -bool BasicFileLauncher::launchWithApp(GAppInfo* app, const FilePathList& paths, GAppLaunchContext* ctx) { - GList* uris = nullptr; - for(auto& path : paths) { - auto uri = path.uri(); - uris = g_list_prepend(uris, uri.release()); - } - GErrorPtr err; - bool ret = bool(g_app_info_launch_uris(app, uris, ctx, &err)); - g_list_foreach(uris, reinterpret_cast(g_free), nullptr); - g_list_free(uris); - if(!ret) { - // FIXME: show error for all files - showError(ctx, err, paths[0]); - } - return ret; -} - - -bool BasicFileLauncher::launchDesktopEntry(const FileInfoPtr &fileInfo, const FilePathList &paths, GAppLaunchContext* ctx) { - /* treat desktop entries as executables */ - auto target = fileInfo->target(); - CStrPtr filename; - const char* desktopEntryName = nullptr; - FilePathList shortcutTargetPaths; - if(fileInfo->isExecutableType()) { - auto act = quickExec_ ? ExecAction::DIRECT_EXEC : askExecFile(fileInfo); - switch(act) { - case ExecAction::EXEC_IN_TERMINAL: - case ExecAction::DIRECT_EXEC: { - if(fileInfo->isShortcut()) { - auto path = handleShortcut(fileInfo, ctx); - if(path.isValid()) { - shortcutTargetPaths.emplace_back(path); - } - } - else { - if(target.empty()) { - filename = fileInfo->path().localPath(); - } - desktopEntryName = !target.empty() ? target.c_str() : filename.get(); - } - break; - } - case ExecAction::OPEN_WITH_DEFAULT_APP: - return launchWithDefaultApp(fileInfo, ctx); - case ExecAction::CANCEL: - return false; - default: - return false; - } - } - /* make exception for desktop entries under menu */ - else if(fileInfo->isNative() /* an exception */ || - fileInfo->path().hasUriScheme("menu")) { - if(target.empty()) { - filename = fileInfo->path().localPath(); - } - desktopEntryName = !target.empty() ? target.c_str() : filename.get(); - } - - if(desktopEntryName) { - return launchDesktopEntry(desktopEntryName, paths, ctx); - } - if(!shortcutTargetPaths.empty()) { - launchPaths(shortcutTargetPaths, ctx); - } - return false; -} - -bool BasicFileLauncher::launchDesktopEntry(const char *desktopEntryName, const FilePathList &paths, GAppLaunchContext *ctx) { - bool ret = false; - GAppInfo* app; - - /* Let GDesktopAppInfo try first. */ - if(g_path_is_absolute(desktopEntryName)) { - app = G_APP_INFO(g_desktop_app_info_new_from_filename(desktopEntryName)); - } - else { - app = G_APP_INFO(g_desktop_app_info_new(desktopEntryName)); - } - /* we handle Type=Link in FmFileInfo so if GIO failed then - it cannot be launched in fact */ - - if(app) { - return launchWithApp(app, paths, ctx); - } - else { - QString msg = QObject::tr("Invalid desktop entry file: '%1'").arg(desktopEntryName); - GErrorPtr err{G_IO_ERROR, G_IO_ERROR_FAILED, msg}; - showError(ctx, err); - } - return ret; -} - -FilePath BasicFileLauncher::handleShortcut(const FileInfoPtr& fileInfo, GAppLaunchContext* ctx) { - auto target = fileInfo->target(); - auto scheme = CStrPtr{g_uri_parse_scheme(target.c_str())}; - if(scheme) { - // collect the uri schemes we support - if(strcmp(scheme.get(), "file") == 0 - || strcmp(scheme.get(), "trash") == 0 - || strcmp(scheme.get(), "network") == 0 - || strcmp(scheme.get(), "computer") == 0) { - return FilePath::fromUri(fileInfo->target().c_str()); - } - else { - // ask gio to launch the default handler for the uri scheme - GAppInfoPtr app{g_app_info_get_default_for_uri_scheme(scheme.get()), false}; - FilePathList uris{FilePath::fromUri(fileInfo->target().c_str())}; - launchWithApp(app.get(), uris, ctx); - } - } - else { - // see it as a local path - return FilePath::fromLocalPath(fileInfo->target().c_str()); - } - return FilePath(); -} - -bool BasicFileLauncher::launchExecutable(const FileInfoPtr &fileInfo, GAppLaunchContext* ctx) { - /* if it's an executable file, directly execute it. */ - auto filename = fileInfo->path().localPath(); - /* FIXME: we need to use eaccess/euidaccess here. */ - if(g_file_test(filename.get(), G_FILE_TEST_IS_EXECUTABLE)) { - auto act = quickExec_ ? ExecAction::DIRECT_EXEC : askExecFile(fileInfo); - int flags = G_APP_INFO_CREATE_NONE; - switch(act) { - case ExecAction::EXEC_IN_TERMINAL: - flags |= G_APP_INFO_CREATE_NEEDS_TERMINAL; - /* Falls through. */ - case ExecAction::DIRECT_EXEC: { - /* filename may contain spaces. Fix #3143296 */ - CStrPtr quoted{g_shell_quote(filename.get())}; - // FIXME: remove libfm dependency - GAppInfoPtr app{fm_app_info_create_from_commandline(quoted.get(), nullptr, GAppInfoCreateFlags(flags), nullptr)}; - if(app) { - CStrPtr run_path{g_path_get_dirname(filename.get())}; - CStrPtr cwd; - /* bug #3589641: scripts are ran from $HOME. - since GIO launcher is kinda ugly - it has - no means to set running directory so we - do workaround - change directory to it */ - if(run_path && strcmp(run_path.get(), ".")) { - cwd = CStrPtr{g_get_current_dir()}; - if(chdir(run_path.get()) != 0) { - cwd.reset(); - // show errors - QString msg = QObject::tr("Cannot set working directory to '%1': %2").arg(run_path.get()).arg(g_strerror(errno)); - GErrorPtr err{G_IO_ERROR, g_io_error_from_errno(errno), msg}; - showError(ctx, err); - } - } - - // FIXME: remove libfm dependency - GErrorPtr err; - if(!fm_app_info_launch(app.get(), nullptr, ctx, &err)) { - showError(ctx, err); - } - if(cwd) { /* return back */ - if(chdir(cwd.get()) != 0) { - g_warning("fm_launch_files(): chdir() failed"); - } - } - return true; - } - break; - } - case ExecAction::OPEN_WITH_DEFAULT_APP: - return launchWithDefaultApp(fileInfo, ctx); - case ExecAction::CANCEL: - default: - break; - } - } - return false; -} - -bool BasicFileLauncher::launchWithDefaultApp(const FileInfoPtr &fileInfo, GAppLaunchContext* ctx) { - FileInfoList files; - files.emplace_back(fileInfo); - GErrorPtr err; - GAppInfoPtr app{g_app_info_get_default_for_type(fileInfo->mimeType()->name(), false), false}; - if(app) { - return launchWithApp(app.get(), files.paths(), ctx); - } - else { - showError(ctx, err, fileInfo->path()); - } - return false; -} - -} // namespace Fm diff --git a/src/core/basicfilelauncher.h b/src/core/basicfilelauncher.h deleted file mode 100644 index a28aa75..0000000 --- a/src/core/basicfilelauncher.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef BASICFILELAUNCHER_H -#define BASICFILELAUNCHER_H - -#include "../libfmqtglobals.h" - -#include "fileinfo.h" -#include "filepath.h" -#include "mimetype.h" - -#include - -namespace Fm { - -class LIBFM_QT_API BasicFileLauncher { -public: - - enum class ExecAction { - NONE, - DIRECT_EXEC, - EXEC_IN_TERMINAL, - OPEN_WITH_DEFAULT_APP, - CANCEL - }; - - explicit BasicFileLauncher(); - virtual ~BasicFileLauncher(); - - bool launchFiles(const FileInfoList &fileInfos, GAppLaunchContext* ctx = nullptr); - - bool launchPaths(FilePathList paths, GAppLaunchContext* ctx = nullptr); - - bool launchDesktopEntry(const FileInfoPtr &fileInfo, const FilePathList& paths = FilePathList{}, GAppLaunchContext* ctx = nullptr); - - bool launchDesktopEntry(const char* desktopEntryName, const FilePathList& paths = FilePathList{}, GAppLaunchContext* ctx = nullptr); - - bool launchWithDefaultApp(const FileInfoPtr& fileInfo, GAppLaunchContext* ctx = nullptr); - - bool launchWithApp(GAppInfo* app, const FilePathList& paths, GAppLaunchContext* ctx = nullptr); - - bool launchExecutable(const FileInfoPtr &fileInfo, GAppLaunchContext* ctx = nullptr); - - bool quickExec() const { - return quickExec_; - } - - void setQuickExec(bool value) { - quickExec_ = value; - } - -protected: - - virtual GAppInfoPtr chooseApp(const FileInfoList& fileInfos, const char* mimeType, GErrorPtr& err); - - virtual bool openFolder(GAppLaunchContext* ctx, const FileInfoList& folderInfos, GErrorPtr& err); - - virtual bool showError(GAppLaunchContext* ctx, GErrorPtr& err, const FilePath& path = FilePath{}, const FileInfoPtr& info = FileInfoPtr{}); - - virtual ExecAction askExecFile(const FileInfoPtr& file); - - virtual int ask(const char* msg, char* const* btn_labels, int default_btn); - -private: - - FilePath handleShortcut(const FileInfoPtr &fileInfo, GAppLaunchContext* ctx = nullptr); - -private: - bool quickExec_; // Don't ask options on launch executable file -}; - -} // namespace Fm - -#endif // BASICFILELAUNCHER_H diff --git a/src/core/bookmarks.cpp b/src/core/bookmarks.cpp deleted file mode 100644 index 93996c8..0000000 --- a/src/core/bookmarks.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include "bookmarks.h" -#include "cstrptr.h" -#include -#include -#include - -namespace Fm { - -std::weak_ptr 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)}; -} - -BookmarkItem::BookmarkItem(const FilePath& path, const QString name): - path_{path}, - name_{name} { - if(name_.isEmpty()) { // if the name is not specified, use basename of the path - name_ = path_.baseName().get(); - } - // We cannot rely on FileInfos to set bookmark icons because there is no guarantee - // that FileInfos already exist, while their creation is costly. Therefore, we have - // to get folder icons directly, as is done at `FileInfo::setFromGFileInfo` and more. - auto local_path = path.localPath(); - auto dot_dir = CStrPtr{g_build_filename(local_path.get(), ".directory", nullptr)}; - if(g_file_test(dot_dir.get(), G_FILE_TEST_IS_REGULAR)) { - GKeyFile* kf = g_key_file_new(); - if(g_key_file_load_from_file(kf, dot_dir.get(), G_KEY_FILE_NONE, nullptr)) { - CStrPtr icon_name{g_key_file_get_string(kf, "Desktop Entry", "Icon", nullptr)}; - if(icon_name) { - icon_ = IconInfo::fromName(icon_name.get()); - } - } - g_key_file_free(kf); - } - if(!icon_ || !icon_->isValid()) { - // first check some standard folders that are shared by Qt and GLib - if(path_ == FilePath::homeDir()) { - icon_ = IconInfo::fromName("user-home"); - } - else if (path_.parent() == FilePath::homeDir()) { - QString folderPath = QString::fromUtf8(path_.toString().get()); - if(folderPath == QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)) { - icon_ = IconInfo::fromName("user-desktop"); - } - else if(folderPath == QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)) { - icon_ = IconInfo::fromName("folder-documents"); - } - else if(folderPath == QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)) { - icon_ = IconInfo::fromName("folder-download"); - } - else if(folderPath == QStandardPaths::writableLocation(QStandardPaths::MusicLocation)) { - icon_ = IconInfo::fromName("folder-music"); - } - else if(folderPath == QStandardPaths::writableLocation(QStandardPaths::PicturesLocation)) { - icon_ = IconInfo::fromName("folder-pictures"); - } - else if(folderPath == QStandardPaths::writableLocation(QStandardPaths::MoviesLocation)) { - icon_ = IconInfo::fromName("folder-videos"); - } - } - // fall back to the default folder icon - if(!icon_ || !icon_->isValid()) { - icon_ = IconInfo::fromName("folder"); - } - } -} - -Bookmarks::Bookmarks(QObject* parent): - QObject(parent), - idle_handler{false} { - - /* 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{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& Bookmarks::insert(const FilePath& path, const QString& name, int pos) { - const auto insert_pos = (pos < 0 || static_cast(pos) > items_.size()) ? items_.cend() : items_.cbegin() + pos; - auto it = items_.insert(insert_pos, std::make_shared(path, name)); - queueSave(); - return *it; -} - -void Bookmarks::remove(const std::shared_ptr& item) { - items_.erase(std::remove(items_.begin(), items_.end(), item), items_.end()); - queueSave(); -} - -void Bookmarks::reorder(const std::shared_ptr& item, int pos) { - auto old_it = std::find(items_.cbegin(), items_.cend(), item); - if(old_it == items_.cend()) - return; - std::shared_ptr 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& item, QString new_name) { - auto it = std::find_if(items_.cbegin(), items_.cend(), [item](const std::shared_ptr& 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(item->path(), new_name)); - items_.erase(it + 1); // remove the old item - queueSave(); - } -} - -std::shared_ptr Bookmarks::globalInstance() { - auto bookmarks = globalInstance_.lock(); - if(!bookmarks) { - bookmarks = std::make_shared(); - 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: - // \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(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 diff --git a/src/core/bookmarks.h b/src/core/bookmarks.h deleted file mode 100644 index 7b5af79..0000000 --- a/src/core/bookmarks.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef FM2_BOOKMARKS_H -#define FM2_BOOKMARKS_H - -#include -#include "gobjectptr.h" -#include "filepath.h" -#include "iconinfo.h" - -namespace Fm { - -class LIBFM_QT_API BookmarkItem { -public: - friend class Bookmarks; - - explicit BookmarkItem(const FilePath& path, const QString name); - - const QString& name() const { - return name_; - } - - const FilePath& path() const { - return path_; - } - - const std::shared_ptr& icon() const { - return icon_; - } - -private: - void setName(const QString& name) { - name_ = name; - } - -private: - FilePath path_; - QString name_; - std::shared_ptr icon_; -}; - - -class LIBFM_QT_API Bookmarks : public QObject { - Q_OBJECT -public: - explicit Bookmarks(QObject* parent = 0); - - ~Bookmarks(); - - const std::shared_ptr &insert(const FilePath& path, const QString& name, int pos); - - void remove(const std::shared_ptr& item); - - void reorder(const std::shared_ptr &item, int pos); - - void rename(const std::shared_ptr& item, QString new_name); - - const std::vector>& items() const { - return items_; - } - - static std::shared_ptr 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 mon; - std::vector> items_; - static std::weak_ptr globalInstance_; - bool idle_handler; -}; - -} // namespace Fm - -#endif // FM2_BOOKMARKS_H diff --git a/src/core/cstrptr.h b/src/core/cstrptr.h deleted file mode 100644 index cc12a0f..0000000 --- a/src/core/cstrptr.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef FM2_CSTRPTR_H -#define FM2_CSTRPTR_H - -#include -#include - -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 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 CStrArrayPtr; - - -} // namespace Fm - -#endif // FM2_CSTRPTR_H diff --git a/src/core/deletejob.cpp b/src/core/deletejob.cpp deleted file mode 100644 index 6c3c550..0000000 --- a/src/core/deletejob.cpp +++ /dev/null @@ -1,158 +0,0 @@ -#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; - } - } - } - - // TODO: get parent dir of the current path. - // if there is a Fm::Folder object created for it, block the update for the folder temporarily. - - /* currently processed file. */ - setCurrentFile(path); - - 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 isTrashRoot = false; - // special handling for trash:/// - if(!path.isNative() && g_strcmp0(path.uriScheme().get(), "trash") == 0) { - // little trick: basename of trash root is / - auto basename = path.baseName(); - if(basename && basename[0] == G_DIR_SEPARATOR) { - isTrashRoot = true; - } - } - - bool hasError = false; - while(!isCancelled()) { - GErrorPtr err; - // try to delete the path directly (but don't delete if it's trash:///) - if(isTrashRoot || 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) { - GErrorPtr err; - GFileEnumeratorPtr enu { - g_file_enumerate_children(path.gfile().get(), defaultGFileInfoQueryAttribs, - 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; -} - - -DeleteJob::DeleteJob(const FilePathList &paths): paths_{paths} { - setCalcProgressUsingSize(false); -} - -DeleteJob::DeleteJob(FilePathList &&paths): paths_{paths} { - setCalcProgressUsingSize(false); -} - -DeleteJob::~DeleteJob() { -} - -void DeleteJob::exec() { - /* prepare the job, count total work needed with FmDeepCountJob */ - TotalSizeJob totalSizeJob{paths_, TotalSizeJob::Flags::PREPARE_DELETE}; - 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 diff --git a/src/core/deletejob.h b/src/core/deletejob.h deleted file mode 100644 index 8ad2321..0000000 --- a/src/core/deletejob.h +++ /dev/null @@ -1,33 +0,0 @@ -#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); - - explicit DeleteJob(FilePathList&& 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 diff --git a/src/core/dirlistjob.cpp b/src/core/dirlistjob.cpp deleted file mode 100644 index caad634..0000000 --- a/src/core/dirlistjob.cpp +++ /dev/null @@ -1,178 +0,0 @@ -#include "dirlistjob.h" -#include -#include "fileinfo_p.h" -#include "gioptrs.h" -#include - -namespace Fm { - -DirListJob::DirListJob(const FilePath& path, Flags _flags, const std::shared_ptr& 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(), defaultGFileInfoQueryAttribs, - 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 lock{mutex_}; - dir_fi = std::make_shared(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(), defaultGFileInfoQueryAttribs, - 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(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 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 diff --git a/src/core/dirlistjob.h b/src/core/dirlistjob.h deleted file mode 100644 index e578521..0000000 --- a/src/core/dirlistjob.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef FM2_DIRLISTJOB_H -#define FM2_DIRLISTJOB_H - -#include "../libfmqtglobals.h" -#include -#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& cutFilesHashSet = nullptr); - - FileInfoList& files() { - return files_; - } - - void setIncremental(bool set); - - bool incremental() const { - return emit_files_found; - } - - FilePath dirPath() const { - std::lock_guard lock{mutex_}; - return dir_path; - } - - std::shared_ptr dirInfo() const { - std::lock_guard 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 dir_fi; - FileInfoList files_; - const std::shared_ptr cutFilesHashSet_; - bool emit_files_found; - // guint delay_add_files_handler; - // GSList* files_to_add; -}; - -} // namespace Fm - -#endif // FM2_DIRLISTJOB_H diff --git a/src/core/filechangeattrjob.cpp b/src/core/filechangeattrjob.cpp deleted file mode 100644 index 8ed2abb..0000000 --- a/src/core/filechangeattrjob.cpp +++ /dev/null @@ -1,324 +0,0 @@ -#include "filechangeattrjob.h" -#include "totalsizejob.h" - -#include - -namespace Fm { - -static const char query[] = G_FILE_ATTRIBUTE_STANDARD_TYPE"," - G_FILE_ATTRIBUTE_STANDARD_NAME"," - G_FILE_ATTRIBUTE_UNIX_GID"," - G_FILE_ATTRIBUTE_UNIX_UID"," - G_FILE_ATTRIBUTE_UNIX_MODE"," - G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME; - -FileChangeAttrJob::FileChangeAttrJob(FilePathList paths): - paths_{std::move(paths)}, - recursive_{false}, - // chmod - fileModeEnabled_{false}, - newMode_{0}, - newModeMask_{0}, - // chown - ownerEnabled_{false}, - uid_{0}, - groupEnabled_{false}, - gid_{0}, - // Display name - displayNameEnabled_{false}, - // icon - iconEnabled_{false}, - // hidden - hiddenEnabled_{false}, - hidden_{false}, - // target uri - targetUriEnabled_{false} { - - // the progress of chmod/chown is not related to file size - setCalcProgressUsingSize(false); -} - -void FileChangeAttrJob::exec() { - // count total amount of the work - if(recursive_) { - TotalSizeJob totalSizeJob{paths_}; - connect(&totalSizeJob, &TotalSizeJob::error, this, &FileChangeAttrJob::error); - connect(this, &FileChangeAttrJob::cancelled, &totalSizeJob, &TotalSizeJob::cancel); - totalSizeJob.run(); - std::uint64_t totalSize, totalCount; - totalSizeJob.totalAmount(totalSize, totalCount); - setTotalAmount(totalSize, totalCount); - } - else { - setTotalAmount(paths_.size(), paths_.size()); - } - - // ready to start - Q_EMIT preparedToRun(); - - // do the actual change attrs job - for(auto& path : paths_) { - if(isCancelled()) { - break; - } - GErrorPtr err; - GFileInfoPtr info{ - g_file_query_info(path.gfile().get(), query, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), &err), - false - }; - if(info) { - processFile(path, info); - } - else { - handleError(err, path, info); - } - } -} - -bool FileChangeAttrJob::processFile(const FilePath& path, const GFileInfoPtr& info) { - setCurrentFile(path); - bool ret = true; - - if(ownerEnabled_) { - changeFileOwner(path, info, uid_); - } - if(groupEnabled_) { - changeFileGroup(path, info, gid_); - } - if(fileModeEnabled_) { - changeFileMode(path, info, newMode_, newModeMask_); - } - /* change display name, icon, hidden, target */ - if(displayNameEnabled_ && !displayName().empty()) { - changeFileDisplayName(path, info, displayName_.c_str()); - } - if(iconEnabled_ && icon_) { - changeFileIcon(path, info, icon_); - } - if(hiddenEnabled_) { - changeFileHidden(path, info, hidden_); - } - if(targetUriEnabled_ && !targetUri_.empty()) { - changeFileTargetUri(path, info, targetUri_.c_str()); - } - - // FIXME: do not use size 1 here. - addFinishedAmount(1, 1); - - // recursively apply to subfolders - auto type = g_file_info_get_file_type(info.get()); - if(!isCancelled() && recursive_ && type == G_FILE_TYPE_DIRECTORY) { - bool retry; - do { - retry = false; - GErrorPtr err; - GFileEnumeratorPtr enu{ - g_file_enumerate_children(path.gfile().get(), query, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), &err), - false - }; - if(enu) { - while(!isCancelled()) { - err.reset(); - GFileInfoPtr childInfo{g_file_enumerator_next_file(enu.get(), cancellable().get(), &err), false}; - if(childInfo) { - auto childPath = path.child(g_file_info_get_name(childInfo.get())); - ret = processFile(childPath, childInfo); - if(!ret) { /* _fm_file_ops_job_change_attr_file() failed */ - break; - } - } - else { - if(err) { - handleError(err, path, info, ErrorSeverity::MILD); - retry = false; - /* FM_JOB_RETRY is not supported here */ - } - else { /* EOF */ - break; - } - } - } - g_file_enumerator_close(enu.get(), cancellable().get(), nullptr); - } - else { - retry = handleError(err, path, info); - } - } while(!isCancelled() && retry); - } - return ret; -} - -bool FileChangeAttrJob::handleError(GErrorPtr &err, const FilePath &path, const GFileInfoPtr &info, ErrorSeverity severity) { - auto act = emitError(err, severity); - if (act == ErrorAction::RETRY) { - err.reset(); - return true; - } - return false; -} - -bool FileChangeAttrJob::changeFileOwner(const FilePath& path, const GFileInfoPtr& info, uid_t uid) { - /* change owner */ - bool ret = false; - bool retry; - do { - GErrorPtr err; - if(!g_file_set_attribute_uint32(path.gfile().get(), G_FILE_ATTRIBUTE_UNIX_UID, - uid, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), &err)) { - retry = handleError(err, path, info, ErrorSeverity::MILD); - err.reset(); - } - else { - ret = true; - retry = false; - } - } while(retry && !isCancelled()); - return ret; -} - -bool FileChangeAttrJob::changeFileGroup(const FilePath& path, const GFileInfoPtr& info, gid_t gid) { - /* change group */ - bool ret = false; - bool retry; - do { - GErrorPtr err; - if(!g_file_set_attribute_uint32(path.gfile().get(), G_FILE_ATTRIBUTE_UNIX_GID, - gid, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), &err)) { - retry = handleError(err, path, info, ErrorSeverity::MILD); - err.reset(); - } - else { - ret = true; - retry = false; - } - } while(retry && !isCancelled()); - return ret; -} - -bool FileChangeAttrJob::changeFileMode(const FilePath& path, const GFileInfoPtr& info, mode_t newMode, mode_t newModeMask) { - bool ret = false; - /* change mode */ - if(newModeMask) { - guint32 mode = g_file_info_get_attribute_uint32(info.get(), G_FILE_ATTRIBUTE_UNIX_MODE); - mode &= ~newModeMask; - mode |= (newMode & newModeMask); - - auto type = g_file_info_get_file_type(info.get()); - /* FIXME: this behavior should be optional. */ - /* treat dirs with 'r' as 'rx' */ - if(type == G_FILE_TYPE_DIRECTORY) { - if((newModeMask & S_IRUSR) && (mode & S_IRUSR)) { - mode |= S_IXUSR; - } - if((newModeMask & S_IRGRP) && (mode & S_IRGRP)) { - mode |= S_IXGRP; - } - if((newModeMask & S_IROTH) && (mode & S_IROTH)) { - mode |= S_IXOTH; - } - } - - /* new mode */ - bool retry; - do { - GErrorPtr err; - if(!g_file_set_attribute_uint32(path.gfile().get(), G_FILE_ATTRIBUTE_UNIX_MODE, - mode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), &err)) { - retry = handleError(err, path, info, ErrorSeverity::MILD); - err.reset(); - } - else { - ret = true; - retry = false; - } - } while(retry && !isCancelled()); - } - return ret; - -} - -bool FileChangeAttrJob::changeFileDisplayName(const FilePath& path, const GFileInfoPtr& info, const char* displayName) { - bool ret = false; - bool retry; - do { - GErrorPtr err; - if(!g_file_set_display_name(path.gfile().get(), displayName, cancellable().get(), &err)) { - retry = handleError(err, path, info, ErrorSeverity::MILD); - err.reset(); - } - else { - ret = true; - retry = false; - } - } while(retry && !isCancelled()); - return ret; -} - -bool FileChangeAttrJob::changeFileIcon(const FilePath& path, const GFileInfoPtr& info, GIconPtr& icon) { - bool ret = false; - bool retry; - do { - GErrorPtr err; - if(!g_file_set_attribute(path.gfile().get(), G_FILE_ATTRIBUTE_STANDARD_ICON, - G_FILE_ATTRIBUTE_TYPE_OBJECT, icon.get(), - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), &err)) { - retry = handleError(err, path, info, ErrorSeverity::MILD); - err.reset(); - } - else { - ret = true; - retry = false; - } - } while(retry && !isCancelled()); - return ret; -} - -bool FileChangeAttrJob::changeFileHidden(const FilePath& path, const GFileInfoPtr& info, bool hidden) { - bool ret = false; - bool retry; - do { - GErrorPtr err; - gboolean g_hidden = hidden; - if(!g_file_set_attribute(path.gfile().get(), G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN, - G_FILE_ATTRIBUTE_TYPE_BOOLEAN, &g_hidden, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), &err)) { - retry = handleError(err, path, info, ErrorSeverity::MILD); - err.reset(); - } - else { - ret = true; - retry = false; - } - } while(retry && !isCancelled()); - return ret; -} - -bool FileChangeAttrJob::changeFileTargetUri(const FilePath& path, const GFileInfoPtr& info, const char* targetUri) { - bool ret = false; - bool retry; - do { - GErrorPtr err; - if(!g_file_set_attribute_string(path.gfile().get(), G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, - targetUri, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), &err)) { - retry = handleError(err, path, info, ErrorSeverity::MILD); - err.reset(); - } - else { - ret = true; - retry = false; - } - } while(retry && !isCancelled()); - return ret; -} - -} // namespace Fm diff --git a/src/core/filechangeattrjob.h b/src/core/filechangeattrjob.h deleted file mode 100644 index 3f15863..0000000 --- a/src/core/filechangeattrjob.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef FM2_FILECHANGEATTRJOB_H -#define FM2_FILECHANGEATTRJOB_H - -#include "../libfmqtglobals.h" -#include "fileoperationjob.h" -#include "gioptrs.h" - -#include - -#include - -namespace Fm { - -class LIBFM_QT_API FileChangeAttrJob : public Fm::FileOperationJob { - Q_OBJECT -public: - explicit FileChangeAttrJob(FilePathList paths); - - void setFileModeEnabled(bool enabled) { - fileModeEnabled_ = enabled; - } - void setFileMode(mode_t newMode, mode_t newModeMask) { - newMode_ = newMode; - newModeMask_ = newModeMask; - } - - bool ownerEnabled() const { - return ownerEnabled_; - } - void setOwnerEnabled(bool enabled) { - ownerEnabled_ = enabled; - } - void setOwner(uid_t uid) { - uid_ = uid; - } - - bool groupEnabled() const { - return groupEnabled_; - } - void setGroupEnabled(bool groupEnabled) { - groupEnabled_ = groupEnabled; - } - void setGroup(gid_t gid) { - gid_ = gid; - } - - // This only work for change attr jobs. - void setRecursive(bool recursive) { - recursive_ = recursive; - } - - void setHiddenEnabled(bool enabled) { - hiddenEnabled_ = enabled; - } - void setHidden(bool hidden) { - hidden_ = hidden; - } - - bool iconEnabled() const { - return iconEnabled_; - } - void setIconEnabled(bool iconEnabled) { - iconEnabled_ = iconEnabled; - } - - bool displayNameEnabled() const { - return displayNameEnabled_; - } - void setDisplayNameEnabled(bool displayNameEnabled) { - displayNameEnabled_ = displayNameEnabled; - } - - const std::string& displayName() const { - return displayName_; - } - void setDisplayName(const std::string& displayName) { - displayName_ = displayName; - } - - bool targetUriEnabled() const { - return targetUriEnabled_; - } - void setTargetUriEnabled(bool targetUriEnabled) { - targetUriEnabled_ = targetUriEnabled; - } - - const std::string& targetUri() const { - return targetUri_; - } - void setTargetUri(const std::string& value) { - targetUri_ = value; - } - - -protected: - void exec() override; - -private: - bool processFile(const FilePath& path, const GFileInfoPtr& info); - bool handleError(GErrorPtr& err, const FilePath& path, const GFileInfoPtr& info, ErrorSeverity severity = ErrorSeverity::MODERATE); - - bool changeFileOwner(const FilePath& path, const GFileInfoPtr& info, uid_t uid); - bool changeFileGroup(const FilePath& path, const GFileInfoPtr& info, gid_t gid); - bool changeFileMode(const FilePath& path, const GFileInfoPtr& info, mode_t newMode, mode_t newModeMask); - bool changeFileDisplayName(const FilePath& path, const GFileInfoPtr& info, const char* displayName); - bool changeFileIcon(const FilePath& path, const GFileInfoPtr& info, GIconPtr& icon); - bool changeFileHidden(const FilePath& path, const GFileInfoPtr& info, bool hidden); - bool changeFileTargetUri(const FilePath& path, const GFileInfoPtr& info, const char* targetUri_); - -private: - FilePathList paths_; - bool recursive_; - - // chmod - bool fileModeEnabled_; - mode_t newMode_; - mode_t newModeMask_; - - // chown - bool ownerEnabled_; - uid_t uid_; - - bool groupEnabled_; - gid_t gid_; - - // Display name - bool displayNameEnabled_; - std::string displayName_; - - // icon - bool iconEnabled_; - GIconPtr icon_; - - // hidden - bool hiddenEnabled_; - bool hidden_; - - // target uri - bool targetUriEnabled_; - std::string targetUri_; -}; - -} // namespace Fm - -#endif // FM2_FILECHANGEATTRJOB_H diff --git a/src/core/fileinfo.cpp b/src/core/fileinfo.cpp deleted file mode 100644 index 8e86f8d..0000000 --- a/src/core/fileinfo.cpp +++ /dev/null @@ -1,409 +0,0 @@ -#include "fileinfo.h" -#include "fileinfo_p.h" -#include - -namespace Fm { - -const char defaultGFileInfoQueryAttribs[] = "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& inf, const FilePath& parentDirPath) { - dirPath_ = parentDirPath; - const char* tmp, *uri; - GIcon* gicon; - GFileType type; - - if (const char * name = g_file_info_get_name(inf.get())) - name_ = name; - - 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; - } - - isShortcut_ = false; - - /* 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; - } - - switch(type) { - case G_FILE_TYPE_SHORTCUT: - isShortcut_ = true; - /* Falls through. */ - case G_FILE_TYPE_MOUNTABLE: - uri = g_file_info_get_attribute_string(inf.get(), G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); - if(uri) { - 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()); - } - } - /* Falls through. */ - /* 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()); - // g_file_info_get_is_backup() does not cover ".bak" and ".old". - // NOTE: Here, dispName_ is not modified for desktop entries yet. - isBackup_ = g_file_info_get_is_backup(inf.get()) - || dispName_.endsWith(QLatin1String(".bak")) - || dispName_.endsWith(QLatin1String(".old")); - isNameChangeable_ = true; /* GVFS tends to ignore this attribute */ - isIconChangeable_ = isHiddenChangeable_ = false; - if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME)) { - 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& 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(isDesktopEntry()) { - /* treat desktop entries as executables if - they are native and have read permission */ - if(isNative() && (mode_ & (S_IRUSR|S_IRGRP|S_IROTH))) { - if(isShortcut() && !target_.empty()) { - /* handle shortcuts from desktop to menu entries: - first check for entries in /usr/share/applications and such - which may be considered as a safe desktop entry path - then check if that is a shortcut to a native file - otherwise it is a link to a file under menu:// */ - if (!g_str_has_prefix(target_.c_str(), "/usr/share/")) { - auto target = FilePath::fromPathStr(target_.c_str()); - bool is_native = target.isNative(); - if (is_native) { - return true; - } - } - } - else { - return true; - } - } - return false; - } - else if(isText()) { /* g_content_type_can_be_executable reports text files as executables too */ - /* We don't execute remote files nor files in trash */ - if(isNative() && (mode_ & (S_IXOTH | S_IXGRP | S_IXUSR))) { - /* it has executable bits so lets check shell-bang */ - 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 diff --git a/src/core/fileinfo.h b/src/core/fileinfo.h deleted file mode 100644 index 03bf825..0000000 --- a/src/core/fileinfo.h +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (C) 2016 Hong Jen Yee (PCMan) - * - * 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 -#include -#include -#include "../libfmqtglobals.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "gioptrs.h" -#include "filepath.h" -#include "iconinfo.h" -#include "mimetype.h" - - -namespace Fm { - -class FileInfoList; -typedef std::set 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& icon() const { - return icon_; - } - - const std::shared_ptr& mimeType() const { - return mimeType_; - } - - quint64 ctime() const { - return ctime_; - } - - - quint64 atime() const { - return atime_; - } - - quint64 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 S_ISDIR(mode_) || 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_; - } - - QString description() const { - return QString::fromUtf8(mimeType_ ? mimeType_->desc() : ""); - } - - 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& cutFilesHashSet); - - const std::forward_list>& 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_; - quint64 mtime_; - quint64 atime_; - quint64 ctime_; - - uint64_t blksize_; - uint64_t blocks_; - - std::shared_ptr mimeType_; - std::shared_ptr icon_; - std::forward_list> 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 cutFilesHashSet_; - // std::vector> extraData_; -}; - - -class LIBFM_QT_API FileInfoList: public std::vector> { -public: - - bool isSameType() const; - - bool isSameFilesystem() const; - - FilePathList paths() const { - FilePathList ret; - for(auto& file: *this) { - ret.push_back(file->path()); - } - return ret; - } -}; - -// smart pointer to FileInfo objects (once created, FileInfo objects should stay immutable so const is needed here) -typedef std::shared_ptr FileInfoPtr; - -typedef std::pair FileInfoPair; - -} - -Q_DECLARE_METATYPE(std::shared_ptr) - - -#endif // __LIBFM_QT_FM2_FILE_INFO_H__ diff --git a/src/core/fileinfo_p.h b/src/core/fileinfo_p.h deleted file mode 100644 index f7a51a6..0000000 --- a/src/core/fileinfo_p.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef FILEINFO_P_H -#define FILEINFO_P_H - -namespace Fm { - - extern const char defaultGFileInfoQueryAttribs[]; - -} // namespace Fm - -#endif // FILEINFO_P_H diff --git a/src/core/fileinfojob.cpp b/src/core/fileinfojob.cpp deleted file mode 100644 index 3c222af..0000000 --- a/src/core/fileinfojob.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "fileinfojob.h" -#include "fileinfo_p.h" - -namespace Fm { - -FileInfoJob::FileInfoJob(FilePathList paths, FilePathList deletionPaths, FilePath commonDirPath, const std::shared_ptr& cutFilesHashSet): - Job(), - paths_{std::move(paths)}, - deletionPaths_{std::move(deletionPaths)}, - 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(), defaultGFileInfoQueryAttribs, - G_FILE_QUERY_INFO_NONE, cancellable().get(), &err), - false - }; - if(!inf) { - continue; - } - - // Reuse the same dirPath object when the path remains the same (optimize for files in the same dir) - auto dirPath = commonDirPath_.isValid() ? commonDirPath_ : path.parent(); - FileInfo fileInfo(inf, dirPath); - - if(cutFilesHashSet_ - && cutFilesHashSet_->count(fileInfo.path().hash())) { - fileInfo.bindCutFiles(cutFilesHashSet_); - } - - auto fileInfoPtr = std::make_shared(fileInfo); - - results_.push_back(fileInfoPtr); - Q_EMIT gotInfo(path, fileInfoPtr); - } - } -} - -} // namespace Fm diff --git a/src/core/fileinfojob.h b/src/core/fileinfojob.h deleted file mode 100644 index 53a03c5..0000000 --- a/src/core/fileinfojob.h +++ /dev/null @@ -1,46 +0,0 @@ -#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, FilePathList deletionPaths = FilePathList(), FilePath commonDirPath = FilePath(), const std::shared_ptr& cutFilesHashSet = nullptr); - - const FilePathList& paths() const { - return paths_; - } - - const FilePathList& deletionPaths() const { - return deletionPaths_; - } - - const FileInfoList& files() const { - return results_; - } - -Q_SIGNALS: - void gotInfo(const FilePath& path, std::shared_ptr& info); - -protected: - void exec() override; - -private: - FilePathList paths_; - FilePathList deletionPaths_; - FileInfoList results_; - FilePath commonDirPath_; - const std::shared_ptr cutFilesHashSet_; -}; - -} // namespace Fm - -#endif // FM2_FILEINFOJOB_H diff --git a/src/core/filelinkjob.cpp b/src/core/filelinkjob.cpp deleted file mode 100644 index 253768e..0000000 --- a/src/core/filelinkjob.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "filelinkjob.h" - -namespace Fm { - -FileLinkJob::FileLinkJob() { - -} - -} // namespace Fm diff --git a/src/core/filelinkjob.h b/src/core/filelinkjob.h deleted file mode 100644 index 965d089..0000000 --- a/src/core/filelinkjob.h +++ /dev/null @@ -1,16 +0,0 @@ -#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 diff --git a/src/core/filemonitor.cpp b/src/core/filemonitor.cpp deleted file mode 100644 index 68c6b6b..0000000 --- a/src/core/filemonitor.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "filemonitor.h" - -namespace Fm { - -FileMonitor::FileMonitor() { - -} - -} // namespace Fm diff --git a/src/core/filemonitor.h b/src/core/filemonitor.h deleted file mode 100644 index 9a1485d..0000000 --- a/src/core/filemonitor.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef FM2_FILEMONITOR_H -#define FM2_FILEMONITOR_H - -#include "../libfmqtglobals.h" -#include -#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 diff --git a/src/core/fileoperationjob.cpp b/src/core/fileoperationjob.cpp deleted file mode 100644 index c2fbb26..0000000 --- a/src/core/fileoperationjob.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include "fileoperationjob.h" - -namespace Fm { - -FileOperationJob::FileOperationJob(): - hasTotalAmount_{false}, - calcProgressUsingSize_{true}, - 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 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 lock{mutex_}; - if(currentFile_.isValid()) { - path = currentFile_; - totalSize = currentFileSize_; - finishedSize = currentFileFinished_; - } - return currentFile_.isValid(); -} - -double FileOperationJob::progress() const { - std::lock_guard lock{mutex_}; - double finishedRatio; - if(calcProgressUsingSize_) { - finishedRatio = totalSize_ > 0 ? double(finishedSize_ + currentFileFinished_) / totalSize_ : 0.0; - } - else { - finishedRatio = totalCount_ > 0 ? double(finishedCount_) / totalCount_ : 0.0; - } - - if(finishedRatio > 1.0) { - finishedRatio = 1.0; - } - return finishedRatio; -} - -FileOperationJob::FileExistsAction FileOperationJob::askRename(const FileInfo &src, const FileInfo &dest, FilePath &newDest) { - FileExistsAction action = SKIP; - Q_EMIT fileExists(src, dest, action, newDest); - return action; -} - -bool FileOperationJob::finishedAmount(uint64_t& finishedSize, uint64_t& finishedCount) const { - std::lock_guard lock{mutex_}; - if(hasTotalAmount_) { - finishedSize = finishedSize_; - finishedCount = finishedCount_; - } - return hasTotalAmount_; -} - -void FileOperationJob::setTotalAmount(uint64_t fileSize, uint64_t fileCount) { - std::lock_guard lock{mutex_}; - hasTotalAmount_ = true; - totalSize_ = fileSize; - totalCount_ = fileCount; -} - -void FileOperationJob::setFinishedAmount(uint64_t finishedSize, uint64_t finishedCount) { - std::lock_guard lock{mutex_}; - finishedSize_ = finishedSize; - finishedCount_ = finishedCount; -} - -void FileOperationJob::addFinishedAmount(uint64_t finishedSize, uint64_t finishedCount) { - std::lock_guard lock{mutex_}; - finishedSize_ += finishedSize; - finishedCount_ += finishedCount; -} - -FilePath FileOperationJob::currentFile() const { - std::lock_guard lock{mutex_}; - auto ret = currentFile_; - return ret; -} - -void FileOperationJob::setCurrentFile(const FilePath& path) { - std::lock_guard lock{mutex_}; - currentFile_ = path; -} - -void FileOperationJob::setCurrentFileProgress(uint64_t totalSize, uint64_t finishedSize) { - std::lock_guard lock{mutex_}; - currentFileSize_ = totalSize; - currentFileFinished_ = finishedSize; -} - -} // namespace Fm diff --git a/src/core/fileoperationjob.h b/src/core/fileoperationjob.h deleted file mode 100644 index f491a4b..0000000 --- a/src/core/fileoperationjob.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef FM2_FILEOPERATIONJOB_H -#define FM2_FILEOPERATIONJOB_H - -#include "../libfmqtglobals.h" -#include "job.h" -#include -#include -#include -#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(); - - // get total amount of work to do - bool totalAmount(std::uint64_t& fileSize, std::uint64_t& fileCount) const; - - // get currently finished job amount - bool finishedAmount(std::uint64_t& finishedSize, std::uint64_t& finishedCount) const; - - // get the current file - FilePath currentFile() const; - - // get progress of the current file - bool currentFileProgress(FilePath& path, std::uint64_t& totalSize, std::uint64_t& finishedSize) const; - - // is the job calculate progress based on file size or file counts - bool calcProgressUsingSize() const { - return calcProgressUsingSize_; - } - - // get currently finished amount (0.0 to 1.0) - virtual double progress() const; - -Q_SIGNALS: - - void preparedToRun(); - - // 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); - - void setCalcProgressUsingSize(bool value) { - calcProgressUsingSize_ = value; - } - - std::mutex& mutex() { - return mutex_; - } - -private: - bool hasTotalAmount_; - bool calcProgressUsingSize_; - std::uint64_t totalSize_; - std::uint64_t totalCount_; - std::uint64_t finishedSize_; - std::uint64_t finishedCount_; - - FilePath currentFile_; - std::uint64_t currentFileSize_; - std::uint64_t currentFileFinished_; - mutable std::mutex mutex_; -}; - -} // namespace Fm - -#endif // FM2_FILEOPERATIONJOB_H diff --git a/src/core/filepath.cpp b/src/core/filepath.cpp deleted file mode 100644 index 36a7910..0000000 --- a/src/core/filepath.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "filepath.h" -#include -#include -#include - -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 diff --git a/src/core/filepath.h b/src/core/filepath.h deleted file mode 100644 index d33658b..0000000 --- a/src/core/filepath.h +++ /dev/null @@ -1,177 +0,0 @@ -#ifndef FM2_FILEPATH_H -#define FM2_FILEPATH_H - -#include "../libfmqtglobals.h" -#include "gobjectptr.h" -#include "cstrptr.h" -#include -#include -#include - -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() 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_; - static FilePath homeDir_; -}; - -struct FilePathHash { - std::size_t operator() (const FilePath& path) const { - return path.hash(); - } -}; - -typedef std::vector FilePathList; - -} // namespace Fm - -Q_DECLARE_METATYPE(Fm::FilePath) - -#endif // FM2_FILEPATH_H diff --git a/src/core/filesysteminfojob.cpp b/src/core/filesysteminfojob.cpp deleted file mode 100644 index 6c3aa69..0000000 --- a/src/core/filesysteminfojob.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "filesysteminfojob.h" -#include "gobjectptr.h" - -namespace Fm { - -void FileSystemInfoJob::exec() { - GObjectPtr inf = GObjectPtr{ - 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 diff --git a/src/core/filesysteminfojob.h b/src/core/filesysteminfojob.h deleted file mode 100644 index 59c963a..0000000 --- a/src/core/filesysteminfojob.h +++ /dev/null @@ -1,45 +0,0 @@ -#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 diff --git a/src/core/filetransferjob.cpp b/src/core/filetransferjob.cpp deleted file mode 100644 index 266b792..0000000 --- a/src/core/filetransferjob.cpp +++ /dev/null @@ -1,641 +0,0 @@ -#include "filetransferjob.h" -#include "totalsizejob.h" -#include "fileinfo_p.h" - -namespace Fm { - -FileTransferJob::FileTransferJob(FilePathList srcPaths, Mode mode): - FileOperationJob{}, - srcPaths_{std::move(srcPaths)}, - mode_{mode} { -} - -FileTransferJob::FileTransferJob(FilePathList srcPaths, FilePathList destPaths, Mode mode): - FileTransferJob{std::move(srcPaths), mode} { - destPaths_ = std::move(destPaths); -} - -FileTransferJob::FileTransferJob(FilePathList srcPaths, const FilePath& destDirPath, Mode mode): - FileTransferJob{std::move(srcPaths), mode} { - setDestDirPath(destDirPath); -} - -void FileTransferJob::setSrcPaths(FilePathList srcPaths) { - srcPaths_ = std::move(srcPaths); -} - -void FileTransferJob::setDestPaths(FilePathList destPaths) { - destPaths_ = std::move(destPaths); -} - -void FileTransferJob::setDestDirPath(const FilePath& destDirPath) { - destPaths_.clear(); - destPaths_.reserve(srcPaths_.size()); - for(const auto& srcPath: srcPaths_) { - FilePath destPath; - if(mode_ == Mode::LINK && !srcPath.isNative()) { - // special handling for URLs - auto fullBasename = srcPath.baseName(); - char* basename = fullBasename.get(); - char* dname = nullptr; - // if we drop URI query onto native filesystem, omit query part - if(!srcPath.isNative()) { - dname = strchr(basename, '?'); - } - // if basename consist only from query then use first part of it - if(dname == basename) { - basename++; - dname = strchr(basename, '&'); - } - - CStrPtr _basename; - if(dname) { - _basename = CStrPtr{g_strndup(basename, dname - basename)}; - dname = strrchr(_basename.get(), G_DIR_SEPARATOR); - g_debug("cutting '%s' to '%s'", basename, dname ? &dname[1] : _basename.get()); - if(dname) { - basename = &dname[1]; - } - else { - basename = _basename.get(); - } - } - destPath = destDirPath.child(basename); - } - else { - destPath = destDirPath.child(srcPath.baseName().get()); - } - destPaths_.emplace_back(std::move(destPath)); - } -} - -void FileTransferJob::gfileCopyProgressCallback(goffset current_num_bytes, goffset total_num_bytes, FileTransferJob* _this) { - _this->setCurrentFileProgress(total_num_bytes, current_num_bytes); -} - -bool FileTransferJob::moveFileSameFs(const FilePath& srcPath, const GFileInfoPtr& srcInfo, FilePath& destPath) { - int flags = G_FILE_COPY_ALL_METADATA | G_FILE_COPY_NOFOLLOW_SYMLINKS; - GErrorPtr err; - bool retry; - do { - retry = false; - err.reset(); - // do the file operation - if(!g_file_move(srcPath.gfile().get(), destPath.gfile().get(), GFileCopyFlags(flags), cancellable().get(), - nullptr, this, &err)) { - retry = handleError(err, srcPath, srcInfo, destPath, flags); - } - else { - return true; - } - } while(retry && !isCancelled()); - return false; -} - -bool FileTransferJob::copyRegularFile(const FilePath& srcPath, const GFileInfoPtr& srcInfo, FilePath& destPath) { - int flags = G_FILE_COPY_ALL_METADATA | G_FILE_COPY_NOFOLLOW_SYMLINKS; - GErrorPtr err; - bool retry; - do { - retry = false; - err.reset(); - - // reset progress of the current file (only for copy) - auto size = g_file_info_get_size(srcInfo.get()); - setCurrentFileProgress(size, 0); - - // do the file operation - if(!g_file_copy(srcPath.gfile().get(), destPath.gfile().get(), GFileCopyFlags(flags), cancellable().get(), - (GFileProgressCallback)&gfileCopyProgressCallback, this, &err)) { - retry = handleError(err, srcPath, srcInfo, destPath, flags); - } - else { - return true; - } - } while(retry && !isCancelled()); - return false; -} - -bool FileTransferJob::copySpecialFile(const FilePath& srcPath, const GFileInfoPtr& srcInfo, FilePath &destPath) { - bool ret = false; - // only handle FIFO for local files - if(srcPath.isNative() && destPath.isNative()) { - auto src_path = srcPath.localPath(); - struct stat src_st; - int r; - r = lstat(src_path.get(), &src_st); - if(r == 0) { - // Handle FIFO on native file systems. - if(S_ISFIFO(src_st.st_mode)) { - auto dest_path = destPath.localPath(); - if(mkfifo(dest_path.get(), src_st.st_mode) == 0) { - ret = true; - } - } - // FIXME: how about block device, char device, and socket? - } - } - if(!ret) { - GErrorPtr err; - g_set_error(&err, G_IO_ERROR, G_IO_ERROR_FAILED, - ("Cannot copy file '%s': not supported"), - g_file_info_get_display_name(srcInfo.get())); - emitError(err, ErrorSeverity::MODERATE); - } - return ret; -} - -bool FileTransferJob::copyDirContent(const FilePath& srcPath, GFileInfoPtr srcInfo, FilePath& destPath, bool skip) { - bool ret = false; - // copy dir content - GErrorPtr err; - auto enu = GFileEnumeratorPtr{ - g_file_enumerate_children(srcPath.gfile().get(), - defaultGFileInfoQueryAttribs, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), &err), - false}; - if(enu) { - int n_children = 0; - int n_copied = 0; - ret = true; - while(!isCancelled()) { - err.reset(); - GFileInfoPtr inf{g_file_enumerator_next_file(enu.get(), cancellable().get(), &err), false}; - if(inf) { - ++n_children; - const char* name = g_file_info_get_name(inf.get()); - FilePath childPath = srcPath.child(name); - bool child_ret = copyFile(childPath, inf, destPath, name, skip); - if(child_ret) { - ++n_copied; - } - else { - ret = false; - } - } - else { - if(err) { - // fail to read directory content - // NOTE: since we cannot read the source dir, we cannot calculate the progress correctly, either. - emitError(err, ErrorSeverity::MODERATE); - err.reset(); - /* ErrorAction::RETRY is not supported here */ - ret = false; - } - else { /* EOF is reached */ - /* all files are successfully copied. */ - if(isCancelled()) { - ret = false; - } - else { - /* some files are not copied */ - if(n_children != n_copied) { - /* if the copy actions are skipped deliberately, it's ok */ - if(!skip) { - ret = false; - } - } - /* else job->skip_dir_content is true */ - } - break; - } - } - } - g_file_enumerator_close(enu.get(), nullptr, &err); - } - else { - if(err) { - emitError(err, ErrorSeverity::MODERATE); - } - } - return ret; -} - -bool FileTransferJob::makeDir(const FilePath& srcPath, GFileInfoPtr srcInfo, FilePath& destPath) { - if(isCancelled()) { - return false; - } - - bool mkdir_done = false; - do { - GErrorPtr err; - mkdir_done = g_file_make_directory_with_parents(destPath.gfile().get(), cancellable().get(), &err); - if(!mkdir_done) { - if(err->domain == G_IO_ERROR && (err->code == G_IO_ERROR_EXISTS || - err->code == G_IO_ERROR_INVALID_FILENAME || - err->code == G_IO_ERROR_FILENAME_TOO_LONG)) { - GFileInfoPtr destInfo = GFileInfoPtr { - g_file_query_info(destPath.gfile().get(), - defaultGFileInfoQueryAttribs, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), nullptr), - false - }; - if(!destInfo) { - // FIXME: error handling - break; - } - - FilePath newDestPath; - FileExistsAction opt = askRename(FileInfo{srcInfo, srcPath.parent()}, FileInfo{destInfo, destPath.parent()}, newDestPath); - switch(opt) { - case FileOperationJob::RENAME: - destPath = std::move(newDestPath); - break; - case FileOperationJob::SKIP: - /* when a dir is skipped, we need to know its total size to calculate correct progress */ - mkdir_done = true; /* pretend that dir creation succeeded */ - break; - case FileOperationJob::OVERWRITE: - mkdir_done = true; /* pretend that dir creation succeeded */ - break; - case FileOperationJob::CANCEL: - cancel(); - return false; - case FileOperationJob::SKIP_ERROR: ; /* FIXME */ - } - } - else { - ErrorAction act = emitError(err, ErrorSeverity::MODERATE); - if(act != ErrorAction::RETRY) { - break; - } - } - } - } while(!mkdir_done && !isCancelled()); - - bool chmod_done = false; - if(mkdir_done && !isCancelled()) { - mode_t mode = g_file_info_get_attribute_uint32(srcInfo.get(), G_FILE_ATTRIBUTE_UNIX_MODE); - if(mode) { - mode |= (S_IRUSR | S_IWUSR); /* ensure we have rw permission to this file. */ - do { - GErrorPtr err; - // chmod the newly created dir properly - // if(!fm_job_is_cancelled(fmjob) && !job->skip_dir_content) - chmod_done = g_file_set_attribute_uint32(destPath.gfile().get(), - G_FILE_ATTRIBUTE_UNIX_MODE, - mode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), &err); - if(!chmod_done) { - ErrorAction act = emitError(err, ErrorSeverity::MODERATE); - if(act != ErrorAction::RETRY) { - break; - } - /* FIXME: some filesystems may not support this. */ - } - } while(!chmod_done && !isCancelled()); - } - } - return mkdir_done && chmod_done; -} - -bool FileTransferJob::handleError(GErrorPtr &err, const FilePath &srcPath, const GFileInfoPtr &srcInfo, FilePath &destPath, int& flags) { - bool retry = false; - /* handle existing files or file name conflict */ - if(err.domain() == G_IO_ERROR && (err.code() == G_IO_ERROR_EXISTS || - err.code() == G_IO_ERROR_INVALID_FILENAME || - err.code() == G_IO_ERROR_FILENAME_TOO_LONG)) { - flags &= ~G_FILE_COPY_OVERWRITE; - - // get info of the existing file - GFileInfoPtr destInfo = GFileInfoPtr { - g_file_query_info(destPath.gfile().get(), - defaultGFileInfoQueryAttribs, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), nullptr), - false - }; - - // ask the user to rename or overwrite the existing file - if(!isCancelled() && destInfo) { - FilePath newDestPath; - FileExistsAction opt = askRename(FileInfo{srcInfo, srcPath.parent()}, - FileInfo{destInfo, destPath.parent()}, - newDestPath); - switch(opt) { - case FileOperationJob::RENAME: - // try a new file name - if(newDestPath.isValid()) { - destPath = std::move(newDestPath); - // FIXME: handle the error when newDestPath is invalid. - } - retry = true; - break; - case FileOperationJob::OVERWRITE: - // overwrite existing file - flags |= G_FILE_COPY_OVERWRITE; - retry = true; - err.reset(); - break; - case FileOperationJob::CANCEL: - // cancel the whole job. - cancel(); - break; - case FileOperationJob::SKIP: - // skip current file and don't copy it - case FileOperationJob::SKIP_ERROR: ; /* FIXME */ - retry = false; - break; - } - err.reset(); - } - } - - // show error message - if(!isCancelled() && err) { - ErrorAction act = emitError(err, ErrorSeverity::MODERATE); - err.reset(); - if(act == ErrorAction::RETRY) { - // the user wants retry the operation again - retry = true; - } - const bool is_no_space = (err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_NO_SPACE); - /* FIXME: ask to leave partial content? */ - if(is_no_space) { - // run out of disk space. delete the partial content we copied. - g_file_delete(destPath.gfile().get(), cancellable().get(), nullptr); - } - } - return retry; -} - -bool FileTransferJob::processPath(const FilePath& srcPath, const FilePath& destDirPath, const char* destFileName) { - GErrorPtr err; - GFileInfoPtr srcInfo = GFileInfoPtr { - g_file_query_info(srcPath.gfile().get(), - defaultGFileInfoQueryAttribs, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), &err), - false - }; - if(!srcInfo || isCancelled()) { - // FIXME: report error - return false; - } - - bool ret; - switch(mode_) { - case Mode::MOVE: - ret = moveFile(srcPath, srcInfo, destDirPath, destFileName); - break; - case Mode::COPY: { - bool deleteSrc = false; - ret = copyFile(srcPath, srcInfo, destDirPath, destFileName, deleteSrc); - break; - } - case Mode::LINK: - ret = linkFile(srcPath, srcInfo, destDirPath, destFileName); - break; - default: - ret = false; - break; - } - return ret; -} - -bool FileTransferJob::moveFile(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName) { - setCurrentFile(srcPath); - - GErrorPtr err; - GFileInfoPtr destDirInfo = GFileInfoPtr { - g_file_query_info(destDirPath.gfile().get(), - "id::filesystem", - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), &err), - false - }; - - if(!destDirInfo || isCancelled()) { - // FIXME: report errors - return false; - } - - // If src and dest are on the same filesystem, do move. - // Exception: if src FS is trash:///, we always do move - // Otherwise, do copy & delete src files. - auto src_fs = g_file_info_get_attribute_string(srcInfo.get(), "id::filesystem"); - auto dest_fs = g_file_info_get_attribute_string(destDirInfo.get(), "id::filesystem"); - bool ret; - if(src_fs && dest_fs && (strcmp(src_fs, dest_fs) == 0 || g_str_has_prefix(src_fs, "trash"))) { - // src and dest are on the same filesystem - auto destPath = destDirPath.child(destFileName); - ret = moveFileSameFs(srcPath, srcInfo, destPath); - - // increase current progress - // FIXME: it's not appropriate to calculate the progress of move operations using file size - // since the time required to move a file is not related to it's file size. - auto size = g_file_info_get_size(srcInfo.get()); - addFinishedAmount(size, 1); - } - else { - // cross device/filesystem move: copy & delete - ret = copyFile(srcPath, srcInfo, destDirPath, destFileName); - // NOTE: do not need to increase progress here since it's done by copyPath(). - } - return ret; -} - -bool FileTransferJob::copyFile(const FilePath& srcPath, const GFileInfoPtr& srcInfo, const FilePath& destDirPath, const char* destFileName, bool skip) { - setCurrentFile(srcPath); - - auto size = g_file_info_get_size(srcInfo.get()); - bool success = false; - setCurrentFileProgress(size, 0); - - auto destPath = destDirPath.child(destFileName); - auto file_type = g_file_info_get_file_type(srcInfo.get()); - if(!skip) { - switch(file_type) { - case G_FILE_TYPE_DIRECTORY: - success = makeDir(srcPath, srcInfo, destPath); - break; - case G_FILE_TYPE_SPECIAL: - success = copySpecialFile(srcPath, srcInfo, destPath); - break; - default: - success = copyRegularFile(srcPath, srcInfo, destPath); - break; - } - } - else { // skip the file - success = true; - } - - if(success) { - // finish copying the file - addFinishedAmount(size, 1); - setCurrentFileProgress(0, 0); - - // recursively copy dir content - if(file_type == G_FILE_TYPE_DIRECTORY) { - success = copyDirContent(srcPath, srcInfo, destPath, skip); - } - - if(!skip && success && mode_ == Mode::MOVE) { - // delete the source file for cross-filesystem move - GErrorPtr err; - if(g_file_delete(srcPath.gfile().get(), cancellable().get(), &err)) { - // FIXME: add some file size to represent the amount of work need to delete a file - addFinishedAmount(1, 1); - } - else { - success = false; - } - } - } - return success; -} - -bool FileTransferJob::linkFile(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName) { - setCurrentFile(srcPath); - - bool ret = false; - // cannot create links on non-native filesystems - if(!destDirPath.isNative()) { - auto msg = tr("Cannot create a link on non-native filesystem"); - GErrorPtr err{g_error_new_literal(G_IO_ERROR, G_IO_ERROR_FAILED, msg.toUtf8().constData())}; - emitError(err, ErrorSeverity::CRITICAL); - return false; - } - - if(srcPath.isNative()) { - // create symlinks for native files - auto destPath = destDirPath.child(destFileName); - ret = createSymlink(srcPath, srcInfo, destPath); - } - else { - // ensure that the dest file has *.desktop filename extension. - CStrPtr desktopEntryFileName{g_strconcat(destFileName, ".desktop", nullptr)}; - auto destPath = destDirPath.child(desktopEntryFileName.get()); - ret = createShortcut(srcPath, srcInfo, destPath); - } - - // update progress - // FIXME: increase the progress by 1 byte is not appropriate - addFinishedAmount(1, 1); - return ret; -} - -bool FileTransferJob::createSymlink(const FilePath &srcPath, const GFileInfoPtr &srcInfo, FilePath &destPath) { - bool ret = false; - auto src = srcPath.localPath(); - int flags = 0; - GErrorPtr err; - bool retry; - do { - retry = false; - if(flags & G_FILE_COPY_OVERWRITE) { // overwrite existing file - // creating symlink cannot overwrite existing files directly, so we delete the existing file first. - g_file_delete(destPath.gfile().get(), cancellable().get(), nullptr); - } - if(!g_file_make_symbolic_link(destPath.gfile().get(), src.get(), cancellable().get(), &err)) { - retry = handleError(err, srcPath, srcInfo, destPath, flags); - } - else { - ret = true; - break; - } - } while(!isCancelled() && retry); - return ret; -} - -bool FileTransferJob::createShortcut(const FilePath &srcPath, const GFileInfoPtr &srcInfo, FilePath &destPath) { - bool ret = false; - const char* iconName = nullptr; - GIcon* icon = g_file_info_get_icon(srcInfo.get()); - if(icon && G_IS_THEMED_ICON(icon)) { - auto iconNames = g_themed_icon_get_names(G_THEMED_ICON(icon)); - if(iconNames && iconNames[0]) { - iconName = iconNames[0]; - } - } - - CStrPtr srcPathUri; - auto uri = g_file_info_get_attribute_string(srcInfo.get(), G_FILE_ATTRIBUTE_STANDARD_TARGET_URI); - if(!uri) { - srcPathUri = srcPath.uri(); - uri = srcPathUri.get(); - } - - CStrPtr srcPathDispName; - auto name = g_file_info_get_display_name(srcInfo.get()); - if(!name) { - srcPathDispName = srcPath.displayName(); - name = srcPathDispName.get(); - } - - GKeyFile* kf = g_key_file_new(); - if(kf) { - g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TYPE, "Link"); - g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, name); - if(iconName) { - g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, iconName); - } - if(uri) { - g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_URL, uri); - } - gsize contentLen; - CStrPtr content{g_key_file_to_data(kf, &contentLen, nullptr)}; - g_key_file_free(kf); - - int flags = 0; - if(content) { - bool retry; - GErrorPtr err; - do { - retry = false; - if(flags & G_FILE_COPY_OVERWRITE) { // overwrite existing file - g_file_delete(destPath.gfile().get(), cancellable().get(), nullptr); - } - - if(!g_file_replace_contents(destPath.gfile().get(), content.get(), contentLen, nullptr, false, G_FILE_CREATE_NONE, nullptr, cancellable().get(), &err)) { - retry = handleError(err, srcPath, srcInfo, destPath, flags); - err.reset(); - } - else { - ret = true; - } - } while(!isCancelled() && retry); - ret = true; - } - } - return ret; -} - - -void FileTransferJob::exec() { - // calculate the total size of files to copy - auto totalSizeFlags = (mode_ == Mode::COPY ? TotalSizeJob::DEFAULT : TotalSizeJob::PREPARE_MOVE); - TotalSizeJob totalSizeJob{srcPaths_, totalSizeFlags}; - connect(&totalSizeJob, &TotalSizeJob::error, this, &FileTransferJob::error); - connect(this, &FileTransferJob::cancelled, &totalSizeJob, &TotalSizeJob::cancel); - totalSizeJob.run(); - if(isCancelled()) { - return; - } - - // ready to start - setTotalAmount(totalSizeJob.totalSize(), totalSizeJob.fileCount()); - Q_EMIT preparedToRun(); - - if(srcPaths_.size() != destPaths_.size()) { - qWarning("error: srcPaths.size() != destPaths.size() when copying files"); - return; - } - - // copy the files - for(size_t i = 0; i < srcPaths_.size(); ++i) { - if(isCancelled()) { - break; - } - const auto& srcPath = srcPaths_[i]; - const auto& destPath = destPaths_[i]; - auto destDirPath = destPath.parent(); - processPath(srcPath, destDirPath, destPath.baseName().get()); - } -} - - -} // namespace Fm diff --git a/src/core/filetransferjob.h b/src/core/filetransferjob.h deleted file mode 100644 index 7c3d8a0..0000000 --- a/src/core/filetransferjob.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef FM2_COPYJOB_H -#define FM2_COPYJOB_H - -#include "../libfmqtglobals.h" -#include "fileoperationjob.h" -#include "gioptrs.h" - -namespace Fm { - -class LIBFM_QT_API FileTransferJob : public Fm::FileOperationJob { - Q_OBJECT -public: - - enum class Mode { - COPY, - MOVE, - LINK - }; - - explicit FileTransferJob(FilePathList srcPaths, Mode mode = Mode::COPY); - explicit FileTransferJob(FilePathList srcPaths, FilePathList destPaths, Mode mode = Mode::COPY); - explicit FileTransferJob(FilePathList srcPaths, const FilePath &destDirPath, Mode mode = Mode::COPY); - - void setSrcPaths(FilePathList srcPaths); - void setDestPaths(FilePathList destPaths); - void setDestDirPath(const FilePath &destDirPath); - -protected: - void exec() override; - -private: - bool processPath(const FilePath& srcPath, const FilePath& destPath, const char *destFileName); - bool moveFile(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName); - bool copyFile(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName, bool skip = false); - bool linkFile(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName); - - bool moveFileSameFs(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath &destPath); - bool copyRegularFile(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath &destPath); - bool copySpecialFile(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath& destPath); - bool copyDirContent(const FilePath &srcPath, GFileInfoPtr srcInfo, FilePath &destPath, bool skip = false); - bool makeDir(const FilePath &srcPath, GFileInfoPtr srcInfo, FilePath &destPath); - bool createSymlink(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath& destPath); - bool createShortcut(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath& destPath); - - bool handleError(GErrorPtr& err, const FilePath &srcPath, const GFileInfoPtr &srcInfo, FilePath &destPath, int& flags); - - static void gfileCopyProgressCallback(goffset current_num_bytes, goffset total_num_bytes, FileTransferJob* _this); - -private: - FilePathList srcPaths_; - FilePathList destPaths_; - Mode mode_; -}; - - -} // namespace Fm - -#endif // FM2_COPYJOB_H diff --git a/src/core/folder.cpp b/src/core/folder.cpp deleted file mode 100644 index 66d4b7b..0000000 --- a/src/core/folder.cpp +++ /dev/null @@ -1,914 +0,0 @@ -/* - * fm-folder.c - * - * Copyright 2009 - 2012 Hong Jen Yee (PCMan) - * Copyright 2012-2016 Andriy Grytsenko (LStranger) - * - * 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 -#include -#include -#include - -#include "dirlistjob.h" -#include "filesysteminfojob.h" -#include "fileinfojob.h" - -namespace Fm { - -std::unordered_map, FilePathHash> Folder::cache_; -QString Folder::cutFilesDirPath_; -QString Folder::lastCutFilesDirPath_; -std::shared_ptr 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 lock{mutex_}; - auto it = cache_.find(dirPath_); - if(it != cache_.end()) { - cache_.erase(it); - } -} - -// static -std::shared_ptr Folder::fromPath(const FilePath& path) { - std::lock_guard 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(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 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& 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(sender()); - fileinfoJobs_.erase(std::find(fileinfoJobs_.cbegin(), fileinfoJobs_.cend(), job)); - - if(job->isCancelled()) - return; - - FileInfoList files_to_add; - FileInfoList files_to_delete; - std::vector files_to_update; - - const auto& paths = job->paths(); - const auto& deletionPaths = job->deletionPaths(); - const auto& infos = job->files(); - auto path_it = paths.cbegin(); - auto info_it = infos.cbegin(); - 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; - } - // add/update the file only if it isn't going to be deleted - else if(std::find(deletionPaths.cbegin(), deletionPaths.cend(), path) == deletionPaths.cend()) { - auto it = files_.find(info->name()); - if(it != files_.end()) { // the file already exists, update - files_to_update.push_back(std::make_pair(it->second, info)); - } - 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); - } - // deletion should be done now, after the info job is processed - for(const auto &path: deletionPaths) { - auto name = path.baseName(); - auto it = files_.find(name.get()); - if(it != files_.end()) { - files_to_delete.push_back(it->second); - files_.erase(it); - } - } - if(!files_to_delete.empty()) { - Q_EMIT filesRemoved(files_to_delete); - } - Q_EMIT contentChanged(); -} - -void Folder::processPendingChanges() { - has_idle_update_handler = false; - // FmFileInfoJob* job = nullptr; - std::lock_guard 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() || !paths_to_del.empty()) { - FilePathList paths, deletionPaths; - paths.insert(paths.end(), paths_to_add.cbegin(), paths_to_add.cend()); - paths.insert(paths.end(), paths_to_update.cbegin(), paths_to_update.cend()); - deletionPaths.insert(deletionPaths.end(), paths_to_del.cbegin(), paths_to_del.cend()); - info_job = new FileInfoJob{paths, deletionPaths, dirPath_, - hasCutFiles() ? cutFilesHashSet_ : nullptr}; - paths_to_update.clear(); - paths_to_add.clear(); - paths_to_del.clear(); - } - - if(info_job) { - 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(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, addition or deletion */ - if(std::find(paths_to_update.cbegin(), paths_to_update.cend(), path) == paths_to_update.cend() - && std::find(paths_to_add.cbegin(), paths_to_add.cend(), path) == paths_to_add.cend() - && std::find(paths_to_del.cbegin(), paths_to_del.cend(), path) == paths_to_del.cend()) { - /* Since this function is called only when a file already exists, even if that file - isn't included in "files_" yet, it will be soon due to a previous call to queueUpdate(). - So, here, we should queue it for changes regardless of what "files_" may contain. */ - 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); - /* Queue the file for deletion only if it is not in the addition queue but - if it is, remove it from that queue instead of queueing it for deletion. - Moreover, as was the case with eventFileChanged(), here too queueing - should be done regardless of what "files_" may contain. */ - if(std::find(paths_to_add.cbegin(), paths_to_add.cend(), path) == paths_to_add.cend()) { - if(std::find(paths_to_del.cbegin(), paths_to_del.cend(), path) == paths_to_del.cend()) { - paths_to_del.push_back(path); - } - } - else { - paths_to_add.erase(std::remove(paths_to_add.begin(), paths_to_add.end(), path), paths_to_add.cend()); - } - /* the update queue should be canceled for a file that is going to be deleted */ - paths_to_update.erase(std::remove(paths_to_update.begin(), paths_to_update.end(), path), paths_to_update.cend()); - queueUpdate(); - // G_UNLOCK(lists); -} - - -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 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 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: - break; - } - } -} - -// checks whether there were cut files here -// and if there were, invalidates this last cut path -bool Folder::hadCutFilesUnset() { - if(lastCutFilesDirPath_ == dirPath_.toString().get()) { - lastCutFilesDirPath_ = QString(); - return true; - } - return false; -} - -bool Folder::hasCutFiles() { - return cutFilesHashSet_ - && !cutFilesHashSet_->empty() - && cutFilesDirPath_ == dirPath_.toString().get(); -} - -void Folder::setCutFiles(const std::shared_ptr& cutFilesHashSet) { - if(cutFilesHashSet_ && !cutFilesHashSet_->empty()) { - lastCutFilesDirPath_ = cutFilesDirPath_; - } - cutFilesDirPath_ = dirPath_.toString().get(); - cutFilesHashSet_ = cutFilesHashSet; -} - -void Folder::onDirListFinished() { - DirListJob* job = static_cast(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 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(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 diff --git a/src/core/folder.h b/src/core/folder.h deleted file mode 100644 index d2bbb76..0000000 --- a/src/core/folder.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2016 Hong Jen Yee (PCMan) - * - * 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 -#include -#include -#include -#include -#include -#include - -#include -#include -#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 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 fileByName(const char* name) const; - - bool isEmpty() const; - - FileInfoList files() const; - - const FilePath& path() const; - - const std::shared_ptr &info() const; - - bool hadCutFilesUnset(); - - bool hasCutFiles(); - - void setCutFiles(const std::shared_ptr& cutFilesHashSet); - - void forEachFile(std::function&)> func) const { - std::lock_guard 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& 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 dirInfo_; - DirListJob* dirlist_job; - std::vector fileinfoJobs_; - FileSystemInfoJob* fsInfoJob_; - - std::shared_ptr volumeManager_; - - /* for file monitor */ - bool has_idle_reload_handler; - bool has_idle_update_handler; - std::vector paths_to_add; - std::vector paths_to_update; - std::vector 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, std::hash> 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, FilePathHash> cache_; - static QString cutFilesDirPath_; - static QString lastCutFilesDirPath_; - static std::shared_ptr cutFilesHashSet_; - static std::mutex mutex_; -}; - -} - -#endif // __LIBFM_QT_FM2_FOLDER_H__ diff --git a/src/core/gioptrs.h b/src/core/gioptrs.h deleted file mode 100644 index 401424b..0000000 --- a/src/core/gioptrs.h +++ /dev/null @@ -1,137 +0,0 @@ -#ifndef GIOPTRS_H -#define GIOPTRS_H -// define smart pointers for GIO data types - -#include -#include -#include "gobjectptr.h" -#include "cstrptr.h" - -namespace Fm { - -typedef GObjectPtr GFilePtr; -typedef GObjectPtr GFileInfoPtr; -typedef GObjectPtr GFileMonitorPtr; -typedef GObjectPtr GCancellablePtr; -typedef GObjectPtr GFileEnumeratorPtr; - -typedef GObjectPtr GInputStreamPtr; -typedef GObjectPtr GFileInputStreamPtr; -typedef GObjectPtr GOutputStreamPtr; -typedef GObjectPtr GFileOutputStreamPtr; - -typedef GObjectPtr GIconPtr; - -typedef GObjectPtr GVolumeMonitorPtr; -typedef GObjectPtr GVolumePtr; -typedef GObjectPtr GMountPtr; - -typedef GObjectPtr 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 diff --git a/src/core/gobjectptr.h b/src/core/gobjectptr.h deleted file mode 100644 index 333bcc2..0000000 --- a/src/core/gobjectptr.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef FM2_GOBJECTPTR_H -#define FM2_GOBJECTPTR_H - -#include "../libfmqtglobals.h" -#include -#include -#include -#include - -namespace Fm { - -template -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(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(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(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 diff --git a/src/core/iconinfo.cpp b/src/core/iconinfo.cpp deleted file mode 100644 index 7e1f145..0000000 --- a/src/core/iconinfo.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#include "iconinfo.h" -#include "iconinfo_p.h" - -namespace Fm { - -std::unordered_map, IconInfo::GIconHash, IconInfo::GIconEqual> IconInfo::cache_; -std::mutex IconInfo::mutex_; -QList IconInfo::fallbackQicons_; - -static const char* fallbackIconNames[] = { - "unknown", - "application-octet-stream", - "application-x-generic", - "text-x-generic", - nullptr -}; - -static QIcon getFirst(const QList & icons) -{ - for (const auto & icon : icons) { - if (!icon.isNull()) - return icon; - } - return QIcon{}; -} - -IconInfo::IconInfo(const char* name): - gicon_{g_themed_icon_new(name), false} { -} - -IconInfo::IconInfo(const GIconPtr gicon): - gicon_{std::move(gicon)} { -} - -IconInfo::~IconInfo() { -} - -// static -std::shared_ptr IconInfo::fromName(const char* name) { - GObjectPtr gicon{g_themed_icon_new(name), false}; - return fromGIcon(gicon); -} - -// static -std::shared_ptr IconInfo::fromGIcon(GIconPtr gicon) { - if(Q_LIKELY(gicon)) { - std::lock_guard 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(std::move(gicon)); - cache_.insert(std::make_pair(icon->gicon_.get(), icon)); - return icon; - } - return std::shared_ptr{}; -} - -void IconInfo::updateQIcons() { - std::lock_guard lock{mutex_}; - for(auto& elem: cache_) { - auto& info = elem.second; - info->internalQicons_.clear(); - } -} - -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_ = getFirst(internalQicons_); - } - } - } - 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_ = getFirst(internalQicons_); - } - } - } - return !transparent ? qicon_ : qiconTransparent_; -} - -QList IconInfo::qiconsFromNames(const char* const* names) { - QList icons; - // qDebug("names: %p", names); - for(const gchar* const* name = names; *name; ++name) { - // qDebug("icon name=%s", *name); - icons.push_back(QIcon::fromTheme(*name)); - } - return icons; -} - -std::forward_list> IconInfo::emblems() const { - std::forward_list> 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 { - QIcon ret_icon; - if(Q_UNLIKELY(internalQicons_.isEmpty())) { - GIcon* gicon = gicon_.get(); - if(G_IS_EMBLEMED_ICON(gicon_.get())) { - gicon = g_emblemed_icon_get_icon(G_EMBLEMED_ICON(gicon)); - } - if(G_IS_THEMED_ICON(gicon)) { - const gchar* const* names = g_themed_icon_get_names(G_THEMED_ICON(gicon)); - internalQicons_ = qiconsFromNames(names); - } - else if(G_IS_FILE_ICON(gicon)) { - GFile* file = g_file_icon_get_file(G_FILE_ICON(gicon)); - CStrPtr fpath{g_file_get_path(file)}; - internalQicons_.push_back(QIcon(fpath.get())); - } - - } - - ret_icon = getFirst(internalQicons_); - - // fallback to default icon - if(Q_UNLIKELY(ret_icon.isNull())) { - if(Q_UNLIKELY(fallbackQicons_.isEmpty())) { - fallbackQicons_ = qiconsFromNames(fallbackIconNames); - } - ret_icon = getFirst(fallbackQicons_); - } - return ret_icon; -} - -} // namespace Fm diff --git a/src/core/iconinfo.h b/src/core/iconinfo.h deleted file mode 100644 index ad21f0c..0000000 --- a/src/core/iconinfo.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * fm-icon.h - * - * Copyright 2009 Hong Jen Yee (PCMan) - * Copyright 2013 Andriy Grytsenko (LStranger) - * - * 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 -#include "gioptrs.h" -#include -#include -#include -#include -#include - - -namespace Fm { - -class LIBFM_QT_API IconInfo: public std::enable_shared_from_this { -public: - friend class IconEngine; - - explicit IconInfo() {} - - explicit IconInfo(const char* name); - - explicit IconInfo(const GIconPtr gicon); - - ~IconInfo(); - - static std::shared_ptr fromName(const char* name); - - static std::shared_ptr fromGIcon(GIconPtr gicon); - - static std::shared_ptr 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> emblems() const; - - bool isValid() const { - return gicon_ != nullptr; - } - -private: - - static QList qiconsFromNames(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 QList internalQicons_; - - static std::unordered_map, GIconHash, GIconEqual> cache_; - static std::mutex mutex_; - static QList fallbackQicons_; -}; - -} // namespace Fm - -Q_DECLARE_METATYPE(std::shared_ptr) - -#endif /* __FM2_ICON_INFO_H__ */ diff --git a/src/core/iconinfo_p.h b/src/core/iconinfo_p.h deleted file mode 100644 index dd03207..0000000 --- a/src/core/iconinfo_p.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2016 Hong Jen Yee (PCMan) - * - * 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 -#include -#include "../libfmqtglobals.h" -#include "iconinfo.h" -#include - -namespace Fm { - -class IconEngine: public QIconEngine { -public: - - IconEngine(std::shared_ptr 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 info_; - bool transparent_; -}; - -IconEngine::IconEngine(std::shared_ptr 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(data); - args->sizes = info ? info->internalQicon().availableSizes(args->mode, args->state) : QList{}; - break; - } - case QIconEngine::IconNameHook: { - QString* result = reinterpret_cast(data); - *result = info ? info->internalQicon().name() : QString{}; - break; - } -#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) - case QIconEngine::IsNullHook: { - bool* result = reinterpret_cast(data); - *result = info ? info->internalQicon().isNull() : true; - break; - } -#endif - } -} - -} // namespace Fm - -#endif // FM_ICONENGINE_H diff --git a/src/core/job.cpp b/src/core/job.cpp deleted file mode 100644 index e597975..0000000 --- a/src/core/job.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#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 diff --git a/src/core/job.h b/src/core/job.h deleted file mode 100644 index 00ca4a5..0000000 --- a/src/core/job.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2016 Hong Jen Yee (PCMan) - * - * 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 -#include -#include -#include -#include -#include -#include -#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__ diff --git a/src/core/job_p.h b/src/core/job_p.h deleted file mode 100644 index d893d5f..0000000 --- a/src/core/job_p.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef JOB_P_H -#define JOB_P_H - -#include -#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 diff --git a/src/core/mimetype.cpp b/src/core/mimetype.cpp deleted file mode 100644 index 19345bb..0000000 --- a/src/core/mimetype.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "mimetype.h" -#include - -#include -#include - -using namespace std; - -namespace Fm { - -std::unordered_map, CStrHash, CStrEqual> MimeType::cache_; -std::mutex MimeType::mutex_; - -std::shared_ptr MimeType::inodeDirectory_; // inode/directory -std::shared_ptr MimeType::inodeShortcut_; // inode/x-shortcut -std::shared_ptr MimeType::inodeMountPoint_; // inode/mount-point -std::shared_ptr MimeType::desktopEntry_; // application/x-desktop - - -MimeType::MimeType(const char* typeName): - name_{g_strdup(typeName)}, - desc_{nullptr} { - - GObjectPtr 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 MimeType::fromName(const char* typeName) { - std::shared_ptr ret; - std::lock_guard lock(mutex_); - auto it = cache_.find(typeName); - if(it == cache_.end()) { - ret = std::make_shared(typeName); - cache_.insert(std::make_pair(ret->name_.get(), ret)); - } - else { - ret = it->second; - } - return ret; -} - -// static -std::shared_ptr 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 diff --git a/src/core/mimetype.h b/src/core/mimetype.h deleted file mode 100644 index 887ce08..0000000 --- a/src/core/mimetype.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * fm-mime-type.h - * - * Copyright 2009 - 2012 Hong Jen Yee (PCMan) - * - * 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 -#include - -#include -#include -#include -#include -#include -#include -#include - -#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 firstThumbnailer() const { - std::lock_guard lock{mutex_}; - return thumbnailers_.empty() ? nullptr : thumbnailers_.front(); - } - - void forEachThumbnailer(std::function&)> func) const { - std::lock_guard lock{mutex_}; - for(auto& thumbnailer: thumbnailers_) { - if(func(thumbnailer)) { - break; - } - } - } - - const std::shared_ptr& 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 fromName(const char* typeName); - - static std::shared_ptr 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 inodeDirectory() { // inode/directory - if(!inodeDirectory_) - inodeDirectory_ = fromName("inode/directory"); - return inodeDirectory_; - } - - static std::shared_ptr inodeShortcut() { // inode/x-shortcut - if(!inodeShortcut_) - inodeShortcut_ = fromName("inode/x-shortcut"); - return inodeShortcut_; - } - - static std::shared_ptr inodeMountPoint() { // inode/mount-point - if(!inodeMountPoint_) - inodeMountPoint_ = fromName("inode/mount-point"); - return inodeMountPoint_; - } - - static std::shared_ptr desktopEntry() { // application/x-desktop - if(!desktopEntry_) - desktopEntry_ = fromName("application/x-desktop"); - return desktopEntry_; - } - -private: - void removeThumbnailer(std::shared_ptr& thumbnailer) { - std::lock_guard lock{mutex_}; - thumbnailers_.remove(thumbnailer); - } - - void addThumbnailer(std::shared_ptr thumbnailer) { - std::lock_guard lock{mutex_}; - thumbnailers_.push_front(std::move(thumbnailer)); - } - -private: - std::shared_ptr icon_; - CStrPtr name_; - mutable CStrPtr desc_; - std::forward_list> thumbnailers_; - static std::unordered_map, CStrHash, CStrEqual> cache_; - static std::mutex mutex_; - - static std::shared_ptr inodeDirectory_; // inode/directory - static std::shared_ptr inodeShortcut_; // inode/x-shortcut - static std::shared_ptr inodeMountPoint_; // inode/mount-point - static std::shared_ptr desktopEntry_; // application/x-desktop -}; - - -} // namespace Fm - -#endif diff --git a/src/core/templates.cpp b/src/core/templates.cpp deleted file mode 100644 index 082f83b..0000000 --- a/src/core/templates.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#include "templates.h" -#include "gioptrs.h" - -#include -#include - -using namespace std; - -namespace Fm { - -std::weak_ptr Templates::globalInstance_; - -TemplateItem::TemplateItem(std::shared_ptr file): fileInfo_{file} { -} - -FilePath TemplateItem::filePath() const { - auto& target = fileInfo_->target(); - if(fileInfo_->isDesktopEntry() && !target.empty()) { - if(target[0] == '/') { // target is an absolute path - return FilePath::fromLocalPath(target.c_str()); - } - else { // resolve relative path - return fileInfo_->dirPath().relativePath(target.c_str()); - } - } - return fileInfo_->path(); -} - -Templates::Templates() : QObject() { - auto* data_dirs = g_get_system_data_dirs(); - // system-wide template dirs - for(auto data_dir = data_dirs; *data_dir; ++data_dir) { - CStrPtr dir_name{g_build_filename(*data_dir, "templates", nullptr)}; - addTemplateDir(dir_name.get()); - } - - // user-specific template dir - CStrPtr dir_name{g_build_filename(g_get_user_data_dir(), "templates", nullptr)}; - addTemplateDir(dir_name.get()); - - // $XDG_TEMPLATES_DIR (FIXME: this might change at runtime) - const gchar *special_dir = g_get_user_special_dir(G_USER_DIRECTORY_TEMPLATES); - if (special_dir) { - addTemplateDir(special_dir); - } -} - -shared_ptr Templates::globalInstance() { - auto templates = globalInstance_.lock(); - if(!templates) { - templates = make_shared(); - globalInstance_ = templates; - } - return templates; -} - -void Templates::addTemplateDir(const char* dirPathName) { - auto dir_path = FilePath::fromLocalPath(dirPathName); - if(dir_path.isValid()) { - auto folder = Folder::fromPath(dir_path); - connect(folder.get(), &Folder::filesAdded, this, &Templates::onFilesAdded); - connect(folder.get(), &Folder::filesChanged, this, &Templates::onFilesChanged); - connect(folder.get(), &Folder::filesRemoved, this, &Templates::onFilesRemoved); - connect(folder.get(), &Folder::removed, this, &Templates::onTemplateDirRemoved); - templateFolders_.emplace_back(std::move(folder)); - } -} - -void Templates::onFilesAdded(FileInfoList& addedFiles) { - for(auto& file : addedFiles) { - // FIXME: we do not support subdirs right now (only XFCE supports this) - if(file->isHidden() || file->isDir()) { - continue; - } - items_.emplace_back(std::make_shared(file)); - // emit a signal for the addition - Q_EMIT itemAdded(items_.back()); - } -} - -void Templates::onFilesChanged(std::vector& changePairs) { - for(auto& change: changePairs) { - auto& old_file = change.first; - auto& new_file = change.second; - auto it = std::find_if(items_.begin(), items_.end(), [&](const std::shared_ptr& item) { - return item->fileInfo() == old_file; - }); - if(it != items_.end()) { - // emit a signal for the change - auto old = *it; - *it = std::make_shared(new_file); - Q_EMIT itemChanged(old, *it); - } - } -} - -void Templates::onFilesRemoved(FileInfoList& removedFiles) { - for(auto& file : removedFiles) { - auto filePath = file->path(); - auto it = std::remove_if(items_.begin(), items_.end(), [&](const std::shared_ptr& item) { - return item->fileInfo() == file; - }); - for(auto removed_it = it; it != items_.end(); ++it) { - // emit a signal for the removal - Q_EMIT itemRemoved(*removed_it); - } - items_.erase(it, items_.end()); - } -} - -void Templates::onTemplateDirRemoved() { - // the whole template dir is removed - auto folder = static_cast(sender()); - if(!folder) { - return; - } - auto dirPath = folder->path(); - - // remote all files under this dir - auto it = std::remove_if(items_.begin(), items_.end(), [&](const std::shared_ptr& item) { - return dirPath.isPrefixOf(item->filePath()); - }); - for(auto removed_it = it; it != items_.end(); ++it) { - // emit a signal for the removal - Q_EMIT itemRemoved(*removed_it); - } - items_.erase(it, items_.end()); -} - -} // namespace Fm diff --git a/src/core/templates.h b/src/core/templates.h deleted file mode 100644 index 6f656dd..0000000 --- a/src/core/templates.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef TEMPLATES_H -#define TEMPLATES_H - -#include "../libfmqtglobals.h" - -#include -#include -#include -#include "folder.h" -#include "fileinfo.h" -#include "mimetype.h" -#include "iconinfo.h" - -namespace Fm { - -class LIBFM_QT_API TemplateItem { -public: - explicit TemplateItem(std::shared_ptr fileInfo); - - QString displayName() const { - return fileInfo_->displayName(); - } - - const std::string& name() const { - return fileInfo_->name(); - } - - std::shared_ptr icon() const { - return fileInfo_->icon(); - } - - std::shared_ptr fileInfo() const { - return fileInfo_; - } - - std::shared_ptr mimeType() const { - return fileInfo_->mimeType(); - } - - FilePath filePath() const; - -private: - std::shared_ptr fileInfo_; -}; - - -class LIBFM_QT_API Templates : public QObject { - Q_OBJECT -public: - explicit Templates(); - - // FIXME: the first call to this method will get no templates since dir loading is in progress. - static std::shared_ptr globalInstance(); - - void forEachItem(std::function&)> func) const { - for(const auto& item : items_) { - func(item); - } - } - - std::vector> items() const { - std::vector> tmp_items; - for(auto& item: items_) { - tmp_items.emplace_back(item); - } - return tmp_items; - } - - bool hasTemplates() const { - return !items_.empty(); - } - -Q_SIGNALS: - void itemAdded(const std::shared_ptr& item); - - void itemChanged(const std::shared_ptr& oldItem, const std::shared_ptr& newItem); - - void itemRemoved(const std::shared_ptr& item); - -private: - void addTemplateDir(const char* dirPathName); - -private Q_SLOTS: - void onFilesAdded(FileInfoList& addedFiles); - - void onFilesChanged(std::vector& changePairs); - - void onFilesRemoved(FileInfoList& removedFiles); - - void onTemplateDirRemoved(); - -private: - std::vector> items_; - std::vector> templateFolders_; - static std::weak_ptr globalInstance_; -}; - -} // namespace Fm - -#endif // TEMPLATES_H diff --git a/src/core/terminal.cpp b/src/core/terminal.cpp deleted file mode 100644 index ae65f3d..0000000 --- a/src/core/terminal.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "terminal.h" - -namespace Fm { - -#include -#include -#include -#include - -#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 allKnownTerminals() { - std::vector 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 diff --git a/src/core/terminal.h b/src/core/terminal.h deleted file mode 100644 index 86c3f4b..0000000 --- a/src/core/terminal.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef TERMINAL_H -#define TERMINAL_H - -#include "../libfmqtglobals.h" -#include "gioptrs.h" -#include "filepath.h" -#include - -namespace Fm { - -LIBFM_QT_API bool launchTerminal(const char* programName, const FilePath& workingDir, GErrorPtr& error); - -LIBFM_QT_API std::vector allKnownTerminals(); - -} // namespace Fm - -#endif // TERMINAL_H diff --git a/src/core/thumbnailer.cpp b/src/core/thumbnailer.cpp deleted file mode 100644 index ab2c80d..0000000 --- a/src/core/thumbnailer.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#include "thumbnailer.h" -#include "mimetype.h" -#include -#include -#include -#include - -namespace Fm { - -std::mutex Thumbnailer::mutex_; -std::vector> 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: - * https://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& 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 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 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(base_name.c_str(), kf); - if(thumbnailer->exec_) { - char** mime_types = g_key_file_get_string_list(kf, "Thumbnailer Entry", "MimeType", nullptr, nullptr); - if(mime_types) { - for(char** name = mime_types; *name; ++name) { - auto mime_type = MimeType::fromName(*name); - if(mime_type) { - std::const_pointer_cast(mime_type)->addThumbnailer(thumbnailer); - } - } - g_strfreev(mime_types); - } - } - allThumbnailers_.push_back(std::move(thumbnailer)); - } - } - g_key_file_free(kf); - } -} - -} // namespace Fm diff --git a/src/core/thumbnailer.h b/src/core/thumbnailer.h deleted file mode 100644 index 91a21d7..0000000 --- a/src/core/thumbnailer.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef FM2_THUMBNAILER_H -#define FM2_THUMBNAILER_H - -#include "../libfmqtglobals.h" -#include "cstrptr.h" -#include -#include -#include -#include - -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> mimeTypes_; - - static std::mutex mutex_; - static std::vector> allThumbnailers_; -}; - -} // namespace Fm - -#endif // FM2_THUMBNAILER_H diff --git a/src/core/thumbnailjob.cpp b/src/core/thumbnailjob.cpp deleted file mode 100644 index 21e256a..0000000 --- a/src/core/thumbnailjob.cpp +++ /dev/null @@ -1,266 +0,0 @@ -#include "thumbnailjob.h" -#include -#include -#include -#include -#include -#include -#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 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 &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(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& 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& file, const QImage &thumbnail) const { - QString thumb_mtime = thumbnail.text("Thumb::MTime"); - return (thumb_mtime.isEmpty() || thumb_mtime.toULongLong() != 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: - * https://www.impulseadventure.com/photo/exif-orientation.html */ - ExifEntry* orient_ent = exif_data_get_entry(exif_data, EXIF_TAG_ORIENTATION); - if(orient_ent) { /* orientation flag found in EXIF */ - gushort orient; - 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& 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& 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 diff --git a/src/core/thumbnailjob.h b/src/core/thumbnailjob.h deleted file mode 100644 index 33dbe0a..0000000 --- a/src/core/thumbnailjob.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef FM2_THUMBNAILJOB_H -#define FM2_THUMBNAILJOB_H - -#include "../libfmqtglobals.h" -#include "fileinfo.h" -#include "gioptrs.h" -#include "job.h" -#include - -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& results() const { - return results_; - } - -Q_SIGNALS: - void thumbnailLoaded(const std::shared_ptr& file, int size, QImage thumbnail); - -protected: - - void exec() override; - -private: - - bool isSupportedImageType(const std::shared_ptr& mimeType) const; - - bool isThumbnailOutdated(const std::shared_ptr& file, const QImage& thumbnail) const; - - QImage generateThumbnail(const std::shared_ptr& file, const FilePath& origPath, const char* uri, const QString& thumbnailFilename); - - QImage readImageFromStream(GInputStream* stream, size_t len); - - QImage loadForFile(const std::shared_ptr& file); - - bool readJpegExif(GInputStream* stream, QImage& thumbnail, int& rotate_degrees); - -private: - FileInfoList files_; - int size_; - std::vector results_; - GCancellablePtr cancellable_; - GChecksum* md5Calc_; - - static QThreadPool* threadPool_; - - static bool localFilesOnly_; - static int maxThumbnailFileSize_; -}; - -} // namespace Fm - -#endif // FM2_THUMBNAILJOB_H diff --git a/src/core/totalsizejob.cpp b/src/core/totalsizejob.cpp deleted file mode 100644 index 090be86..0000000 --- a/src/core/totalsizejob.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#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); - if(fs_id && dest_fs_id && (strcmp(fs_id, dest_fs_id) == 0 || g_str_has_prefix(fs_id, "trash"))) { - // same filesystem or move from trash:/// - descend = false; - } - else { - /* files on different device requires an additional 'delete' for the source file. */ - ++totalSize_; /* this is for the additional delete */ - ++totalOndiskSize_; - ++fileCount_; - descend = true; - } - } - - if(type == G_FILE_TYPE_DIRECTORY) { - /* check if we need to decends into the dir. */ - /* trash:/// doesn't support deleting files recursively (but we want to descend into trash root "trash:///" */ - if(flags_ & PREPARE_DELETE && path.hasUriScheme("trash") && path.baseName()[0] != '/') { - descend = false; - } - else { - /* only descends into files on the same filesystem */ - if(flags_ & SAME_FS) { - fs_id = g_file_info_get_attribute_string(inf.get(), G_FILE_ATTRIBUTE_ID_FILESYSTEM); - descend = (g_strcmp0(fs_id, dest_fs_id) == 0); - } - } - - 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 diff --git a/src/core/totalsizejob.h b/src/core/totalsizejob.h deleted file mode 100644 index 43c19c0..0000000 --- a/src/core/totalsizejob.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef FM2_TOTALSIZEJOB_H -#define FM2_TOTALSIZEJOB_H - -#include "../libfmqtglobals.h" -#include "fileoperationjob.h" -#include "filepath.h" -#include -#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 diff --git a/src/core/trashjob.cpp b/src/core/trashjob.cpp deleted file mode 100644 index 18996a9..0000000 --- a/src/core/trashjob.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "trashjob.h" - -namespace Fm { - -TrashJob::TrashJob(FilePathList paths): paths_{std::move(paths)} { - // calculate progress using finished file counts rather than their sizes - setCalcProgressUsingSize(false); -} - -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); - - // TODO: get parent dir of the current path. - // if there is a Fm::Folder object created for it, block the update for the folder temporarily. - - for(;;) { // retry the i/o operation on errors - auto gf = path.gfile(); - bool ret = false; - // FIXME: do not depend on fm_config - if(fm_config->no_usb_trash) { - GMountPtr mnt{g_file_find_enclosing_mount(gf.get(), nullptr, nullptr), false}; - if(mnt) { - ret = g_mount_can_unmount(mnt.get()); /* TRUE if it's removable media */ - if(ret) { - unsupportedFiles_.push_back(path); - break; // don't trash the file - } - } - } - - // move the file to trash - GErrorPtr err; - ret = g_file_trash(gf.get(), cancellable().get(), &err); - if(ret) { // trash operation succeeded - break; - } - else { // failed - // if trashing is not supported by the file system - if(err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_NOT_SUPPORTED) { - unsupportedFiles_.push_back(path); - } - 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 diff --git a/src/core/trashjob.h b/src/core/trashjob.h deleted file mode 100644 index 65d93cf..0000000 --- a/src/core/trashjob.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef FM2_TRASHJOB_H -#define FM2_TRASHJOB_H - -#include "../libfmqtglobals.h" -#include "fileoperationjob.h" -#include "filepath.h" - -namespace Fm { - -class LIBFM_QT_API TrashJob : public Fm::FileOperationJob { - Q_OBJECT -public: - explicit TrashJob(FilePathList paths); - - FilePathList unsupportedFiles() const { - return unsupportedFiles_; - } - -protected: - - void exec() override; - -private: - FilePathList paths_; - FilePathList unsupportedFiles_; -}; - -} // namespace Fm - -#endif // FM2_TRASHJOB_H diff --git a/src/core/untrashjob.cpp b/src/core/untrashjob.cpp deleted file mode 100644 index 031509e..0000000 --- a/src/core/untrashjob.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "untrashjob.h" -#include "filetransferjob.h" - -namespace Fm { - -UntrashJob::UntrashJob(FilePathList srcPaths): - srcPaths_{std::move(srcPaths)} { -} - -void UntrashJob::exec() { - // preparing for the job - FilePathList validSrcPaths; - FilePathList origPaths; - for(auto& srcPath: srcPaths_) { - if(isCancelled()) { - break; - } - GErrorPtr err; - GFileInfoPtr srcInfo{ - g_file_query_info(srcPath.gfile().get(), - "trash::orig-path", - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable().get(), - &err), - false - }; - if(srcInfo) { - const char* orig_path_str = g_file_info_get_attribute_byte_string(srcInfo.get(), "trash::orig-path"); - if(orig_path_str) { - validSrcPaths.emplace_back(srcPath); - origPaths.emplace_back(FilePath::fromPathStr(orig_path_str)); - } - else { - ErrorAction act; - g_set_error(&err, G_IO_ERROR, G_IO_ERROR_FAILED, - tr("Cannot untrash file '%s': original path not known").toUtf8().constData(), - g_file_info_get_display_name(srcInfo.get())); - // FIXME: do we need to retry here? - emitError(err, ErrorSeverity::MODERATE); - } - } - else { - // FIXME: do we need to retry here? - emitError(err); - } - } - - // collected original paths of the trashed files - // use the file transfer job to handle the actual file move - FileTransferJob fileTransferJob{std::move(validSrcPaths), std::move(origPaths), FileTransferJob::Mode::MOVE}; - // FIXME: - // I'm not sure why specifying Qt::DirectConnection is needed here since the caller & receiver are in the same thread. :-( - // However without this, the signals/slots here will cause deadlocks. - connect(&fileTransferJob, &FileTransferJob::preparedToRun, this, &UntrashJob::preparedToRun, Qt::DirectConnection); - connect(&fileTransferJob, &FileTransferJob::error, this, &UntrashJob::error, Qt::DirectConnection); - connect(&fileTransferJob, &FileTransferJob::fileExists, this, &UntrashJob::fileExists, Qt::DirectConnection); - - // cancel the file transfer subjob if the parent job is cancelled - connect(this, &UntrashJob::cancelled, &fileTransferJob, - [&fileTransferJob]() { - if(!fileTransferJob.isCancelled()) { - fileTransferJob.cancel(); - } - }, Qt::DirectConnection); - - // cancel the parent job if the file transfer subjob is cancelled - connect(&fileTransferJob, &FileTransferJob::cancelled, this, - [this]() { - if(!isCancelled()) { - cancel(); - } - }, Qt::DirectConnection); - fileTransferJob.run(); -} - -} // namespace Fm diff --git a/src/core/untrashjob.h b/src/core/untrashjob.h deleted file mode 100644 index 22a5129..0000000 --- a/src/core/untrashjob.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef FM2_UNTRASHJOB_H -#define FM2_UNTRASHJOB_H - -#include "../libfmqtglobals.h" -#include "fileoperationjob.h" - -namespace Fm { - -class LIBFM_QT_API UntrashJob : public FileOperationJob { -public: - explicit UntrashJob(FilePathList srcPaths); - -protected: - void exec() override; - -private: - FilePathList srcPaths_; -}; - -} // namespace Fm - -#endif // FM2_UNTRASHJOB_H diff --git a/src/core/userinfocache.cpp b/src/core/userinfocache.cpp deleted file mode 100644 index 1c237f0..0000000 --- a/src/core/userinfocache.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "userinfocache.h" -#include -#include - -namespace Fm { - -UserInfoCache* UserInfoCache::globalInstance_ = nullptr; -std::mutex UserInfoCache::mutex_; - -UserInfoCache::UserInfoCache() : QObject() { -} - -const std::shared_ptr& UserInfoCache::userFromId(uid_t uid) { - std::lock_guard lock{mutex_}; - auto it = users_.find(uid); - if(it != users_.end()) - return it->second; - std::shared_ptr user; - auto pw = getpwuid(uid); - if(pw) { - user = std::make_shared(uid, pw->pw_name, pw->pw_gecos); - } - return (users_[uid] = user); -} - -const std::shared_ptr& UserInfoCache::groupFromId(gid_t gid) { - std::lock_guard lock{mutex_}; - auto it = groups_.find(gid); - if(it != groups_.end()) - return it->second; - std::shared_ptr group; - auto gr = getgrgid(gid); - if(gr) { - group = std::make_shared(gid, gr->gr_name); - } - return (groups_[gid] = group); -} - -// static -UserInfoCache* UserInfoCache::globalInstance() { - std::lock_guard lock{mutex_}; - if(!globalInstance_) - globalInstance_ = new UserInfoCache(); - return globalInstance_; -} - -} // namespace Fm diff --git a/src/core/userinfocache.h b/src/core/userinfocache.h deleted file mode 100644 index 7338463..0000000 --- a/src/core/userinfocache.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef FM2_USERINFOCACHE_H -#define FM2_USERINFOCACHE_H - -#include "../libfmqtglobals.h" -#include -#include -#include -#include -#include -#include - -namespace Fm { - -class LIBFM_QT_API UserInfo { -public: - explicit UserInfo(uid_t uid, const char* name, const char* realName): - uid_{uid}, name_{name}, realName_{realName} { - } - - uid_t uid() const { - return uid_; - } - - const QString& name() const { - return name_; - } - - const QString& realName() const { - return realName_; - } - -private: - uid_t uid_; - QString name_; - QString realName_; - -}; - -class LIBFM_QT_API GroupInfo { -public: - explicit GroupInfo(gid_t gid, const char* name): gid_{gid}, name_{name} { - } - - gid_t gid() const { - return gid_; - } - - const QString& name() const { - return name_; - } - -private: - gid_t gid_; - QString name_; -}; - -// FIXME: handle file changes - -class LIBFM_QT_API UserInfoCache : public QObject { - Q_OBJECT -public: - explicit UserInfoCache(); - - const std::shared_ptr& userFromId(uid_t uid); - - const std::shared_ptr& groupFromId(gid_t gid); - - static UserInfoCache* globalInstance(); - -Q_SIGNALS: - void changed(); - -private: - std::unordered_map> users_; - std::unordered_map> groups_; - static UserInfoCache* globalInstance_; - static std::mutex mutex_; -}; - -} // namespace Fm - -#endif // FM2_USERINFOCACHE_H diff --git a/src/core/volumemanager.cpp b/src/core/volumemanager.cpp deleted file mode 100644 index 522c38c..0000000 --- a/src/core/volumemanager.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "volumemanager.h" - -namespace Fm { - -std::mutex VolumeManager::mutex_; -std::weak_ptr VolumeManager::globalInstance_; - -VolumeManager::VolumeManager(): - QObject(), - monitor_{g_volume_monitor_get(), false} { - - // connect gobject signal handlers - g_signal_connect(monitor_.get(), "volume-added", G_CALLBACK(_onGVolumeAdded), this); - g_signal_connect(monitor_.get(), "volume-removed", G_CALLBACK(_onGVolumeRemoved), this); - g_signal_connect(monitor_.get(), "volume-changed", G_CALLBACK(_onGVolumeChanged), this); - - g_signal_connect(monitor_.get(), "mount-added", G_CALLBACK(_onGMountAdded), this); - g_signal_connect(monitor_.get(), "mount-removed", G_CALLBACK(_onGMountRemoved), this); - g_signal_connect(monitor_.get(), "mount-changed", G_CALLBACK(_onGMountChanged), this); - - // g_get_volume_monitor() is a slow blocking call, so call it in a low priority thread - auto job = new GetGVolumeMonitorJob(); - job->setAutoDelete(true); - connect(job, &GetGVolumeMonitorJob::finished, this, &VolumeManager::onGetGVolumeMonitorFinished, Qt::BlockingQueuedConnection); - job->runAsync(QThread::LowPriority); -} - -VolumeManager::~VolumeManager() { - if(monitor_) { - g_signal_handlers_disconnect_by_data(monitor_.get(), this); - } -} - -std::shared_ptr VolumeManager::globalInstance() { - std::lock_guard lock{mutex_}; - auto mon = globalInstance_.lock(); - if(mon == nullptr) { - mon = std::make_shared(); - globalInstance_ = mon; - } - return mon; -} - -void VolumeManager::onGetGVolumeMonitorFinished() { - auto job = static_cast(sender()); - monitor_ = std::move(job->monitor_); - GList* vols = g_volume_monitor_get_volumes(monitor_.get()); - for(GList* l = vols; l != nullptr; l = l->next) { - volumes_.push_back(Volume{G_VOLUME(l->data), false}); - Q_EMIT volumeAdded(volumes_.back()); - } - g_list_free(vols); - - GList* mnts = g_volume_monitor_get_mounts(monitor_.get()); - for(GList* l = mnts; l != nullptr; l = l->next) { - mounts_.push_back(Mount{G_MOUNT(l->data), false}); - Q_EMIT mountAdded(mounts_.back()); - } - g_list_free(mnts); -} - -void VolumeManager::onGVolumeAdded(GVolume* vol) { - if(std::find(volumes_.cbegin(), volumes_.cend(), vol) != volumes_.cend()) - return; - volumes_.push_back(Volume{vol, true}); - Q_EMIT volumeAdded(volumes_.back()); -} - -void VolumeManager::onGVolumeRemoved(GVolume* vol) { - auto it = std::find(volumes_.begin(), volumes_.end(), vol); - if(it == volumes_.end()) - return; - Q_EMIT volumeRemoved(*it); - volumes_.erase(it); -} - -void VolumeManager::onGVolumeChanged(GVolume* vol) { - auto it = std::find(volumes_.begin(), volumes_.end(), vol); - if(it == volumes_.end()) - return; - Q_EMIT volumeChanged(*it); -} - -void VolumeManager::onGMountAdded(GMount* mnt) { - if(std::find(mounts_.cbegin(), mounts_.cend(), mnt) != mounts_.cend()) - return; - mounts_.push_back(Mount{mnt, true}); - Q_EMIT mountAdded(mounts_.back()); -} - -void VolumeManager::onGMountRemoved(GMount* mnt) { - auto it = std::find(mounts_.begin(), mounts_.end(), mnt); - if(it == mounts_.end()) - return; - Q_EMIT mountRemoved(*it); - mounts_.erase(it); -} - -void VolumeManager::onGMountChanged(GMount* mnt) { - auto it = std::find(mounts_.begin(), mounts_.end(), mnt); - if(it == mounts_.end()) - return; - Q_EMIT mountChanged(*it); -} - -void VolumeManager::GetGVolumeMonitorJob::exec() { - monitor_ = GVolumeMonitorPtr{g_volume_monitor_get(), false}; -} - - -} // namespace Fm diff --git a/src/core/volumemanager.h b/src/core/volumemanager.h deleted file mode 100644 index 642adc8..0000000 --- a/src/core/volumemanager.h +++ /dev/null @@ -1,237 +0,0 @@ -#ifndef FM2_VOLUMEMANAGER_H -#define FM2_VOLUMEMANAGER_H - -#include "../libfmqtglobals.h" -#include -#include -#include "gioptrs.h" -#include "filepath.h" -#include "iconinfo.h" -#include "job.h" -#include -#include - -namespace Fm { - -class LIBFM_QT_API Volume: public GVolumePtr { -public: - - explicit Volume(GVolume* gvol, bool addRef): GVolumePtr{gvol, addRef} { - } - - explicit Volume(GVolumePtr gvol): GVolumePtr{std::move(gvol)} { - } - - CStrPtr name() const { - return CStrPtr{g_volume_get_name(get())}; - } - - CStrPtr uuid() const { - return CStrPtr{g_volume_get_uuid(get())}; - } - - std::shared_ptr icon() const { - return IconInfo::fromGIcon(GIconPtr{g_volume_get_icon(get()), false}); - } - - // GDrive * g_volume_get_drive(get()); - GMountPtr mount() const { - return GMountPtr{g_volume_get_mount(get()), false}; - } - - bool canMount() const { - return g_volume_can_mount(get()); - } - - bool shouldAutoMount() const { - return g_volume_should_automount(get()); - } - - FilePath activationRoot() const { - return FilePath{g_volume_get_activation_root(get()), false}; - } - - /* - void g_volume_mount(get()); - gboolean g_volume_mount_finish(get()); - */ - bool canEject() const { - return g_volume_can_eject(get()); - } - - /* - void g_volume_eject(get()); - gboolean g_volume_eject_finish(get()); - void g_volume_eject_with_operation(get()); - gboolean g_volume_eject_with_operation_finish(get()); - char ** g_volume_enumerate_identifiers(get()); - char * g_volume_get_identifier(get()); - const gchar * g_volume_get_sort_key(get()); - */ - - CStrPtr device() const { - return CStrPtr{g_volume_get_identifier(get(), G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE)}; - } - - CStrPtr label() const { - return CStrPtr{g_volume_get_identifier(get(), G_VOLUME_IDENTIFIER_KIND_LABEL)}; - } - -}; - - -class LIBFM_QT_API Mount: public GMountPtr { -public: - - explicit Mount(GMount* mnt, bool addRef): GMountPtr{mnt, addRef} { - } - - explicit Mount(GMountPtr gmnt): GMountPtr{std::move(gmnt)} { - } - - CStrPtr name() const { - return CStrPtr{g_mount_get_name(get())}; - } - - CStrPtr uuid() const { - return CStrPtr{g_mount_get_uuid(get())}; - } - - std::shared_ptr icon() const { - return IconInfo::fromGIcon(GIconPtr{g_mount_get_icon(get()), false}); - } - - // GIcon * g_mount_get_symbolic_icon(get()); - // GDrive * g_mount_get_drive(get()); - FilePath root() const { - return FilePath{g_mount_get_root(get()), false}; - } - - GVolumePtr volume() const { - return GVolumePtr{g_mount_get_volume(get()), false}; - } - - FilePath defaultLocation() const { - return FilePath{g_mount_get_default_location(get()), false}; - } - - bool canUnmount() const { - return g_mount_can_unmount(get()); - } - -/* - void g_mount_unmount(get()); - gboolean g_mount_unmount_finish(get()); - void g_mount_unmount_with_operation(get()); - gboolean g_mount_unmount_with_operation_finish(get()); - void g_mount_remount(get()); - gboolean g_mount_remount_finish(get()); -*/ - bool canEject() const { - return g_mount_can_eject(get()); - } - -/* - void g_mount_eject(get()); - gboolean g_mount_eject_finish(get()); - void g_mount_eject_with_operation(get()); - gboolean g_mount_eject_with_operation_finish(get()); -*/ - // void g_mount_guess_content_type(get()); - // gchar ** g_mount_guess_content_type_finish(get()); - // gchar ** g_mount_guess_content_type_sync(get()); - - bool isShadowed() const { - return g_mount_is_shadowed(get()); - } - - // void g_mount_shadow(get()); - // void g_mount_unshadow(get()); - // const gchar * g_mount_get_sort_key(get()); -}; - - - -class LIBFM_QT_API VolumeManager : public QObject { - Q_OBJECT -public: - explicit VolumeManager(); - - ~VolumeManager(); - - const std::vector& volumes() const { - return volumes_; - } - - const std::vector& mounts() const { - return mounts_; - } - - static std::shared_ptr globalInstance(); - -Q_SIGNALS: - void volumeAdded(const Volume& vol); - void volumeRemoved(const Volume& vol); - void volumeChanged(const Volume& vol); - - void mountAdded(const Mount& mnt); - void mountRemoved(const Mount& mnt); - void mountChanged(const Mount& mnt); - -public Q_SLOTS: - - void onGetGVolumeMonitorFinished(); - -private: - - class GetGVolumeMonitorJob: public Job { - public: - GetGVolumeMonitorJob() {} - GVolumeMonitorPtr monitor_; - protected: - void exec() override; - }; - - static void _onGVolumeAdded(GVolumeMonitor* /*mon*/, GVolume* vol, VolumeManager* _this) { - _this->onGVolumeAdded(vol); - } - void onGVolumeAdded(GVolume* vol); - - static void _onGVolumeRemoved(GVolumeMonitor* /*mon*/, GVolume* vol, VolumeManager* _this) { - _this->onGVolumeRemoved(vol); - } - void onGVolumeRemoved(GVolume* vol); - - static void _onGVolumeChanged(GVolumeMonitor* /*mon*/, GVolume* vol, VolumeManager* _this) { - _this->onGVolumeChanged(vol); - } - void onGVolumeChanged(GVolume* vol); - - static void _onGMountAdded(GVolumeMonitor* /*mon*/, GMount* mnt, VolumeManager* _this) { - _this->onGMountAdded(mnt); - } - void onGMountAdded(GMount* mnt); - - static void _onGMountRemoved(GVolumeMonitor* /*mon*/, GMount* mnt, VolumeManager* _this) { - _this->onGMountRemoved(mnt); - } - void onGMountRemoved(GMount* mnt); - - static void _onGMountChanged(GVolumeMonitor* /*mon*/, GMount* mnt, VolumeManager* _this) { - _this->onGMountChanged(mnt); - } - void onGMountChanged(GMount* mnt); - -private: - GVolumeMonitorPtr monitor_; - - std::vector volumes_; - std::vector mounts_; - - static std::mutex mutex_; - static std::weak_ptr globalInstance_; -}; - -} // namespace Fm - -#endif // FM2_VOLUMEMANAGER_H diff --git a/src/createnewmenu.cpp b/src/createnewmenu.cpp deleted file mode 100644 index 227982e..0000000 --- a/src/createnewmenu.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 "createnewmenu.h" -#include "folderview.h" -#include "utilities.h" -#include "core/iconinfo.h" -#include "core/templates.h" - -#include - -namespace Fm { - - -class TemplateAction: public QAction { -public: - TemplateAction(std::shared_ptr item, QObject* parent): - QAction(parent) { - setTemplateItem(std::move(item)); - } - - const std::shared_ptr templateItem() const { - return templateItem_; - } - - void setTemplateItem(std::shared_ptr item) { - templateItem_ = std::move(item); - auto mimeType = templateItem_->mimeType(); - setText(QString("%1 (%2)").arg(templateItem_->displayName()).arg(mimeType->desc())); - setIcon(templateItem_->icon()->qicon()); - } - -private: - std::shared_ptr templateItem_; -}; - - -CreateNewMenu::CreateNewMenu(QWidget* dialogParent, Fm::FilePath dirPath, QWidget* parent): - QMenu(parent), - dialogParent_(dialogParent), - dirPath_(std::move(dirPath)), - templateSeparator_{nullptr}, - templates_{Templates::globalInstance()} { - - QAction* action = new QAction(QIcon::fromTheme("folder-new"), tr("Folder"), this); - connect(action, &QAction::triggered, this, &CreateNewMenu::onCreateNewFolder); - addAction(action); - - action = new QAction(QIcon::fromTheme("document-new"), tr("Blank File"), this); - connect(action, &QAction::triggered, this, &CreateNewMenu::onCreateNewFile); - addAction(action); - - // add more items to "Create New" menu from templates - connect(templates_.get(), &Templates::itemAdded, this, &CreateNewMenu::addTemplateItem); - connect(templates_.get(), &Templates::itemChanged, this, &CreateNewMenu::updateTemplateItem); - connect(templates_.get(), &Templates::itemRemoved, this, &CreateNewMenu::removeTemplateItem); - templates_->forEachItem([this](const std::shared_ptr& item) { - addTemplateItem(item); - }); -} - -CreateNewMenu::~CreateNewMenu() { -} - -void CreateNewMenu::onCreateNewFile() { - if(dirPath_) { - createFileOrFolder(CreateNewTextFile, dirPath_); - } -} - -void CreateNewMenu::onCreateNewFolder() { - if(dirPath_) { - createFileOrFolder(CreateNewFolder, dirPath_); - } -} - -void CreateNewMenu::onCreateNew() { - TemplateAction* action = static_cast(sender()); - if(dirPath_) { - createFileOrFolder(CreateWithTemplate, dirPath_, action->templateItem().get(), dialogParent_); - } -} - -void CreateNewMenu::addTemplateItem(const std::shared_ptr &item) { - if(!templateSeparator_) { - templateSeparator_= addSeparator(); - } - auto mimeType = item->mimeType(); - /* we support directories differently */ - if(mimeType->isDir()) { - return; - } - QAction* action = new TemplateAction{item, this}; - connect(action, &QAction::triggered, this, &CreateNewMenu::onCreateNew); - addAction(action); -} - -void CreateNewMenu::updateTemplateItem(const std::shared_ptr &oldItem, const std::shared_ptr &newItem) { - auto allActions = actions(); - auto separatorPos = allActions.indexOf(templateSeparator_); - // all items after the separator are templates - for(auto i = separatorPos + 1; i < allActions.size(); ++i) { - auto action = static_cast(allActions[i]); - if(action->templateItem() == oldItem) { - // update the menu item - action->setTemplateItem(newItem); - break; - } - } -} - -void CreateNewMenu::removeTemplateItem(const std::shared_ptr &item) { - if(!templateSeparator_) { - return; - } - auto allActions = actions(); - auto separatorPos = allActions.indexOf(templateSeparator_); - // all items after the separator are templates - for(auto i = separatorPos + 1; i < allActions.size(); ++i) { - auto action = static_cast(allActions[i]); - if(action->templateItem() == item) { - // delete the action from the menu - removeAction(action); - allActions.removeAt(i); - break; - } - } - - // no more template items. remove the separator - if(separatorPos == allActions.size() - 1) { - removeAction(templateSeparator_); - templateSeparator_ = nullptr; - } -} - -} // namespace Fm diff --git a/src/createnewmenu.h b/src/createnewmenu.h deleted file mode 100644 index e6b4d4f..0000000 --- a/src/createnewmenu.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_CREATENEWMENU_H -#define FM_CREATENEWMENU_H - -#include "libfmqtglobals.h" -#include -#include - -#include "core/filepath.h" - -namespace Fm { - -class FolderView; -class Templates; -class TemplateItem; - -class LIBFM_QT_API CreateNewMenu : public QMenu { - Q_OBJECT - -public: - explicit CreateNewMenu(QWidget* dialogParent, Fm::FilePath dirPath, QWidget* parent = 0); - virtual ~CreateNewMenu(); - -protected Q_SLOTS: - void onCreateNewFolder(); - - void onCreateNewFile(); - - void onCreateNew(); - -private Q_SLOTS: - void addTemplateItem(const std::shared_ptr& item); - - void updateTemplateItem(const std::shared_ptr& oldItem, const std::shared_ptr& newItem); - - void removeTemplateItem(const std::shared_ptr& item); - -private: - QWidget* dialogParent_; - Fm::FilePath dirPath_; - QAction* templateSeparator_; - std::shared_ptr templates_; -}; - -} - -#endif // FM_CREATENEWMENU_H diff --git a/src/customaction_p.h b/src/customaction_p.h deleted file mode 100644 index 7aba3f9..0000000 --- a/src/customaction_p.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_CUSTOMACTION_P_H -#define FM_CUSTOMACTION_P_H - -#include -#include "customactions/fileaction.h" - -namespace Fm { - -class CustomAction : public QAction { -public: - explicit CustomAction(std::shared_ptr item, QObject* parent = nullptr): - QAction{QString::fromStdString(item->get_name()), parent}, - item_{item} { - auto& icon_name = item->get_icon(); - if(!icon_name.empty()) { - setIcon(QIcon::fromTheme(icon_name.c_str())); - } - } - - virtual ~CustomAction() { - } - - const std::shared_ptr& item() const { - return item_; - } - -private: - std::shared_ptr item_; -}; - -} // namespace Fm - -#endif diff --git a/src/customactions/fileaction.cpp b/src/customactions/fileaction.cpp deleted file mode 100644 index 31d5e3a..0000000 --- a/src/customactions/fileaction.cpp +++ /dev/null @@ -1,615 +0,0 @@ -#include "fileaction.h" -#include -#include - -using namespace std; - -namespace Fm { - -static const char* desktop_env = nullptr; // current desktop environment -static bool actions_loaded = false; // all actions are loaded? -static unordered_map, CStrHash, CStrEqual> all_actions; // cache all loaded actions - -FileActionObject::FileActionObject() { -} - -FileActionObject::FileActionObject(GKeyFile* kf) { - name = CStrPtr{g_key_file_get_locale_string(kf, "Desktop Entry", "Name", nullptr, nullptr)}; - tooltip = CStrPtr{g_key_file_get_locale_string(kf, "Desktop Entry", "Tooltip", nullptr, nullptr)}; - icon = CStrPtr{g_key_file_get_locale_string(kf, "Desktop Entry", "Icon", nullptr, nullptr)}; - desc = CStrPtr{g_key_file_get_locale_string(kf, "Desktop Entry", "Description", nullptr, nullptr)}; - GErrorPtr err; - enabled = g_key_file_get_boolean(kf, "Desktop Entry", "Enabled", &err); - if(err) { // key not found, default to true - err.reset(); - enabled = true; - } - hidden = g_key_file_get_boolean(kf, "Desktop Entry", "Hidden", nullptr); - suggested_shortcut = CStrPtr{g_key_file_get_string(kf, "Desktop Entry", "SuggestedShortcut", nullptr)}; - - condition = unique_ptr {new FileActionCondition(kf, "Desktop Entry")}; - - has_parent = false; -} - -FileActionObject::~FileActionObject() { -} - -//static -bool FileActionObject::is_plural_exec(const char* exec) { - if(!exec) { - return false; - } - // the first relevent code encountered in Exec parameter - // determines whether the command accepts singular or plural forms - for(int i = 0; exec[i]; ++i) { - char ch = exec[i]; - if(ch == '%') { - ++i; - ch = exec[i]; - switch(ch) { - case 'B': - case 'D': - case 'F': - case 'M': - case 'O': - case 'U': - case 'W': - case 'X': - return true; // plural - case 'b': - case 'd': - case 'f': - case 'm': - case 'o': - case 'u': - case 'w': - case 'x': - return false; // singular - default: - // irrelevent code, skip - break; - } - } - } - return false; // singular form by default -} - -std::string FileActionObject::expand_str(const char* templ, const FileInfoList& files, bool for_display, std::shared_ptr first_file) { - if(!templ) { - return string{}; - } - string result; - - if(!first_file) { - first_file = files.front(); - } - - for(int i = 0; templ[i]; ++i) { - char ch = templ[i]; - if(ch == '%') { - ++i; - ch = templ[i]; - switch(ch) { - case 'b': // (first) basename - if(for_display) { - result += first_file->name(); - } - else { - CStrPtr quoted{g_shell_quote(first_file->name().c_str())}; - result += quoted.get(); - } - break; - case 'B': // space-separated list of basenames - for(auto& fi : files) { - if(for_display) { - result += fi->name(); - } - else { - CStrPtr quoted{g_shell_quote(fi->name().c_str())}; - result += quoted.get(); - } - result += ' '; - } - if(result[result.length() - 1] == ' ') { // remove trailing space - result.erase(result.length() - 1); - } - break; - case 'c': // count of selected items - result += to_string(files.size()); - break; - case 'd': { // (first) base directory - // FIXME: should the base dir be a URI? - auto base_dir = first_file->dirPath(); - auto str = base_dir.toString(); - if(for_display) { - // FIXME: str = Filename.display_name(str); - } - CStrPtr quoted{g_shell_quote(str.get())}; - result += quoted.get(); - break; - } - case 'D': // space-separated list of base directory of each selected items - for(auto& fi : files) { - auto base_dir = fi->dirPath(); - auto str = base_dir.toString(); - if(for_display) { - // str = Filename.display_name(str); - } - CStrPtr quoted{g_shell_quote(str.get())}; - result += quoted.get(); - result += ' '; - } - if(result[result.length() - 1] == ' ') { // remove trailing space - result.erase(result.length() - 1); - } - break; - case 'f': { // (first) file name - auto filename = first_file->path().toString(); - if(for_display) { - // filename = Filename.display_name(filename); - } - CStrPtr quoted{g_shell_quote(filename.get())}; - result += quoted.get(); - break; - } - case 'F': // space-separated list of selected file names - for(auto& fi : files) { - auto filename = fi->path().toString(); - if(for_display) { - // filename = Filename.display_name(filename); - } - CStrPtr quoted{g_shell_quote(filename.get())}; - result += quoted.get(); - result += ' '; - } - if(result[result.length() - 1] == ' ') { // remove trailing space - result.erase(result.length() - 1); - } - break; - case 'h': // hostname of the (first) URI - // FIXME: how to support this correctly? - // FIXME: currently we pass g_get_host_name() - result += g_get_host_name(); - break; - case 'm': // mimetype of the (first) selected item - result += first_file->mimeType()->name(); - break; - case 'M': // space-separated list of the mimetypes of the selected items - for(auto& fi : files) { - result += fi->mimeType()->name(); - result += ' '; - } - break; - case 'n': // username of the (first) URI - // FIXME: how to support this correctly? - result += g_get_user_name(); - break; - case 'o': // no-op operator which forces a singular form of execution when specified as first parameter, - case 'O': // no-op operator which forces a plural form of execution when specified as first parameter, - break; - case 'p': // port number of the (first) URI - // FIXME: how to support this correctly? - // result.append("0"); - break; - case 's': // scheme of the (first) URI - result += first_file->path().uriScheme().get(); - break; - case 'u': // (first) URI - result += first_file->path().uri().get(); - break; - case 'U': // space-separated list of selected URIs - for(auto& fi : files) { - result += fi->path().uri().get(); - result += ' '; - } - if(result[result.length() - 1] == ' ') { // remove trailing space - result.erase(result.length() - 1); - } - break; - case 'w': { // (first) basename without the extension - auto basename = first_file->name(); - int pos = basename.rfind('.'); - // FIXME: handle non-UTF8 filenames - if(pos != -1) { - basename.erase(pos, string::npos); - } - CStrPtr quoted{g_shell_quote(basename.c_str())}; - result += quoted.get(); - break; - } - case 'W': // space-separated list of basenames without their extension - for(auto& fi : files) { - auto basename = fi->name(); - int pos = basename.rfind('.'); - // FIXME: for_display ? Shell.quote(str) : str); - if(pos != -1) { - basename.erase(pos, string::npos); - } - CStrPtr quoted{g_shell_quote(basename.c_str())}; - result += quoted.get(); - result += ' '; - } - if(result[result.length() - 1] == ' ') { // remove trailing space - result.erase(result.length() - 1); - } - break; - case 'x': { // (first) extension - auto basename = first_file->name(); - int pos = basename.rfind('.'); - const char* ext = ""; - if(pos >= 0) { - ext = basename.c_str() + pos + 1; - } - CStrPtr quoted{g_shell_quote(ext)}; - result += quoted.get(); - break; - } - case 'X': // space-separated list of extensions - for(auto& fi : files) { - auto basename = fi->name(); - int pos = basename.rfind('.'); - const char* ext = ""; - if(pos >= 0) { - ext = basename.c_str() + pos + 1; - } - CStrPtr quoted{g_shell_quote(ext)}; - result += quoted.get(); - result += ' '; - } - if(result[result.length() - 1] == ' ') { // remove trailing space - result.erase(result.length() - 1); - } - break; - case '%': // the % character - result += '%'; - break; - case '\0': - break; - } - } - else { - result += ch; - } - } - return result; -} - -FileAction::FileAction(GKeyFile* kf): FileActionObject{kf}, target{FILE_ACTION_TARGET_CONTEXT} { - type = FileActionType::ACTION; - - GErrorPtr err; - if(g_key_file_get_boolean(kf, "Desktop Entry", "TargetContext", &err)) { // default to true - target |= FILE_ACTION_TARGET_CONTEXT; - } - else if(!err) { // error means the key is abscent - target &= ~FILE_ACTION_TARGET_CONTEXT; - } - if(g_key_file_get_boolean(kf, "Desktop Entry", "TargetLocation", nullptr)) { - target |= FILE_ACTION_TARGET_LOCATION; - } - if(g_key_file_get_boolean(kf, "Desktop Entry", "TargetToolbar", nullptr)) { - target |= FILE_ACTION_TARGET_TOOLBAR; - } - toolbar_label = CStrPtr{g_key_file_get_locale_string(kf, "Desktop Entry", "ToolbarLabel", nullptr, nullptr)}; - - auto profile_names = CStrArrayPtr{g_key_file_get_string_list(kf, "Desktop Entry", "Profiles", nullptr, nullptr)}; - if(profile_names != nullptr) { - for(auto profile_name = profile_names.get(); *profile_name; ++profile_name) { - // stdout.printf("%s", profile); - profiles.push_back(make_shared(kf, *profile_name)); - } - } -} - -std::shared_ptr FileAction::match(const FileInfoList& files) const { - //qDebug() << "FileAction.match: " << id.get(); - if(hidden || !enabled) { - return nullptr; - } - - if(!condition->match(files)) { - return nullptr; - } - for(const auto& profile : profiles) { - if(profile->match(files)) { - //qDebug() << " profile matched!\n\n"; - return profile; - } - } - // stdout.printf("\n"); - return nullptr; -} - -FileActionMenu::FileActionMenu(GKeyFile* kf): FileActionObject{kf} { - type = FileActionType::MENU; - items_list = CStrArrayPtr{g_key_file_get_string_list(kf, "Desktop Entry", "ItemsList", nullptr, nullptr)}; -} - -bool FileActionMenu::match(const FileInfoList& files) const { - // stdout.printf("FileActionMenu.match: %s\n", id); - if(hidden || !enabled) { - return false; - } - if(!condition->match(files)) { - return false; - } - // stdout.printf("menu matched!: %s\n\n", id); - return true; -} - -void FileActionMenu::cache_children(const FileInfoList& files, const char** items_list) { - for(; *items_list; ++items_list) { - const char* item_id_prefix = *items_list; - size_t len = strlen(item_id_prefix); - if(item_id_prefix[0] == '[' && item_id_prefix[len - 1] == ']') { - // runtime dynamic item list - char* output; - int exit_status; - string prefix{item_id_prefix + 1, len - 2}; // skip [ and ] - auto command = expand_str(prefix.c_str(), files); - if(g_spawn_command_line_sync(command.c_str(), &output, nullptr, &exit_status, nullptr) && exit_status == 0) { - CStrArrayPtr item_ids{g_strsplit(output, ";", -1)}; - g_free(output); - cache_children(files, (const char**)item_ids.get()); - } - } - else if(strcmp(item_id_prefix, "SEPARATOR") == 0) { - // separator item - cached_children.push_back(nullptr); - } - else { - CStrPtr item_id{g_strconcat(item_id_prefix, ".desktop", nullptr)}; - auto it = all_actions.find(item_id.get()); - if(it != all_actions.end()) { - auto child_action = it->second; - child_action->has_parent = true; - cached_children.push_back(child_action); - // stdout.printf("add child: %s to menu: %s\n", item_id, id); - } - } - } -} - -std::shared_ptr FileActionItem::fromActionObject(std::shared_ptr action_obj, const FileInfoList& files) { - std::shared_ptr item; - if(action_obj->type == FileActionType::MENU) { - auto menu = static_pointer_cast(action_obj); - if(menu->match(files)) { - item = make_shared(menu, files); - // eliminate empty menus - if(item->children.empty()) { - item = nullptr; - } - } - } - else { - // handle profiles here - auto action = static_pointer_cast(action_obj); - auto profile = action->match(files); - if(profile) { - item = make_shared(action, profile, files); - } - } - return item; -} - -FileActionItem::FileActionItem(std::shared_ptr _action, std::shared_ptr _profile, const FileInfoList& files): - FileActionItem{static_pointer_cast(_action), files} { - profile = _profile; -} - -FileActionItem::FileActionItem(std::shared_ptr menu, const FileInfoList& files): - FileActionItem{static_pointer_cast(menu), files} { - for(auto& action_obj : menu->cached_children) { - if(action_obj == nullptr) { // separator - children.push_back(nullptr); - } - else { // action item or menu - auto subitem = fromActionObject(action_obj, files); - if(subitem != nullptr) { - children.push_back(subitem); - } - } - } -} - -FileActionItem::FileActionItem(std::shared_ptr _action, const FileInfoList& files) { - action = std::move(_action); - name = FileActionObject::expand_str(action->name.get(), files, true); - desc = FileActionObject::expand_str(action->desc.get(), files, true); - icon = FileActionObject::expand_str(action->icon.get(), files, false); -} - -bool FileActionItem::launch(GAppLaunchContext* ctx, const FileInfoList& files, CStrPtr& output) const { - if(action->type == FileActionType::ACTION) { - if(profile != nullptr) { - profile->launch(ctx, files, output); - } - return true; - } - return false; -} - -static void load_actions_from_dir(const char* dirname, const char* id_prefix) { - //qDebug() << "loading from: " << dirname << endl; - auto dir = g_dir_open(dirname, 0, nullptr); - if(dir != nullptr) { - for(;;) { - const char* name = g_dir_read_name(dir); - if(name == nullptr) { - break; - } - // found a file in file-manager/actions dir, get its full path - CStrPtr full_path{g_build_filename(dirname, name, nullptr)}; - // stdout.printf("\nfound %s\n", full_path); - - // see if it's a sub dir - if(g_file_test(full_path.get(), G_FILE_TEST_IS_DIR)) { - // load sub dirs recursively - CStrPtr new_id_prefix; - if(id_prefix) { - new_id_prefix = CStrPtr{g_strconcat(id_prefix, "-", name, nullptr)}; - } - load_actions_from_dir(full_path.get(), id_prefix ? new_id_prefix.get() : name); - } - else if(g_str_has_suffix(name, ".desktop")) { - CStrPtr new_id_prefix; - if(id_prefix) { - new_id_prefix = CStrPtr{g_strconcat(id_prefix, "-", name, nullptr)}; - } - const char* id = id_prefix ? new_id_prefix.get() : name; - // ensure that it's not already in the cache - if(all_actions.find(id) == all_actions.cend()) { - auto kf = g_key_file_new(); - if(g_key_file_load_from_file(kf, full_path.get(), G_KEY_FILE_NONE, nullptr)) { - auto type = CStrPtr{g_key_file_get_string(kf, "Desktop Entry", "Type", nullptr)}; - if(!type) { - continue; - } - std::shared_ptr action; - if(strcmp(type.get(), "Action") == 0) { - action = static_pointer_cast(make_shared(kf)); - // stdout.printf("load action: %s\n", id); - } - else if(strcmp(type.get(), "Menu") == 0) { - action = static_pointer_cast(make_shared(kf)); - // stdout.printf("load menu: %s\n", id); - } - else { - continue; - } - action->setId(id); - all_actions.insert(make_pair(action->id.get(), action)); // add the id/action pair to hash table - // stdout.printf("add to cache %s\n", id); - } - g_key_file_free(kf); - } - else { - // stdout.printf("cache found for action: %s\n", id); - } - } - } - g_dir_close(dir); - } -} - -void file_actions_set_desktop_env(const char* env) { - desktop_env = env; -} - -static void load_all_actions() { - all_actions.clear(); - auto dirs = g_get_system_data_dirs(); - for(auto dir = dirs; *dir; ++dir) { - CStrPtr dir_path{g_build_filename(*dir, "file-manager/actions", nullptr)}; - load_actions_from_dir(dir_path.get(), nullptr); - } - CStrPtr dir_path{g_build_filename(g_get_user_data_dir(), "file-manager/actions", nullptr)}; - load_actions_from_dir(dir_path.get(), nullptr); - actions_loaded = true; -} - -bool FileActionItem::compare_items(std::shared_ptr a, std::shared_ptr b) -{ - // first get the list of level-zero item names (http://www.nautilus-actions.org/?q=node/377) - static QStringList itemNamesList; - static bool level_zero_checked = false; - if(!level_zero_checked) { - level_zero_checked = true; - auto level_zero = CStrPtr{g_build_filename(g_get_user_data_dir(), - "file-manager/actions/level-zero.directory", nullptr)}; - if(g_file_test(level_zero.get(), G_FILE_TEST_IS_REGULAR)) { - GKeyFile* kf = g_key_file_new(); - if(g_key_file_load_from_file(kf, level_zero.get(), G_KEY_FILE_NONE, nullptr)) { - auto itemsList = CStrArrayPtr{g_key_file_get_string_list(kf, - "Desktop Entry", - "ItemsList", nullptr, nullptr)}; - if(itemsList) { - for(uint i = 0; i < g_strv_length(itemsList.get()); ++i) { - CStrPtr desktop_file_name{g_strconcat(itemsList.get()[i], ".desktop", nullptr)}; - auto desktop_file = CStrPtr{g_build_filename(g_get_user_data_dir(), - "file-manager/actions", - desktop_file_name.get(), nullptr)}; - GKeyFile* desktop_file_key = g_key_file_new(); - if(g_key_file_load_from_file(desktop_file_key, desktop_file.get(), G_KEY_FILE_NONE, nullptr)) { - auto actionName = CStrPtr{g_key_file_get_string(desktop_file_key, - "Desktop Entry", - "Name", NULL)}; - if(actionName) { - itemNamesList << QString::fromUtf8(actionName.get()); - } - } - g_key_file_free(desktop_file_key); - } - } - } - g_key_file_free(kf); - } - } - if(!itemNamesList.isEmpty()) { - int first = itemNamesList.indexOf(QString::fromStdString(a->get_name())); - int second = itemNamesList.indexOf(QString::fromStdString(b->get_name())); - if(first > -1) { - if(second > -1) { - return (first < second); - } - else { - return true; // list items have priority - } - } - else if(second > -1) { - return false; - } - } - return (a->get_name().compare(b->get_name()) < 0); -} - -FileActionItemList FileActionItem::get_actions_for_files(const FileInfoList& files) { - if(!actions_loaded) { - load_all_actions(); - } - - // Iterate over all actions to establish association between parent menu - // and children actions, and to find out toplevel ones which are not - // attached to any parent menu - for(auto& item : all_actions) { - auto& action_obj = item.second; - // stdout.printf("id = %s\n", action_obj.id); - if(action_obj->type == FileActionType::MENU) { // this is a menu - auto menu = static_pointer_cast(action_obj); - // stdout.printf("menu: %s\n", menu.name); - // associate child items with menus - menu->cache_children(files, (const char**)menu->items_list.get()); - } - } - - // Output the menus - FileActionItemList items; - - for(auto& item : all_actions) { - auto& action_obj = item.second; - // only output toplevel items here - if(action_obj->has_parent == false) { // this is a toplevel item - auto item = FileActionItem::fromActionObject(action_obj, files); - if(item != nullptr) { - items.push_back(item); - } - } - } - - // cleanup temporary data cached during menu generation - for(auto& item : all_actions) { - auto& action_obj = item.second; - action_obj->has_parent = false; - if(action_obj->type == FileActionType::MENU) { - auto menu = static_pointer_cast(action_obj); - menu->cached_children.clear(); - } - } - - std::sort(items.begin(), items.end(), compare_items); - return items; -} - -} // namespace Fm diff --git a/src/customactions/fileaction.h b/src/customactions/fileaction.h deleted file mode 100644 index 4c2a8bc..0000000 --- a/src/customactions/fileaction.h +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef FILEACTION_H -#define FILEACTION_H - -#include -#include - -#include "../core/fileinfo.h" -#include "fileactioncondition.h" -#include "fileactionprofile.h" - -namespace Fm { - -enum class FileActionType { - NONE, - ACTION, - MENU -}; - - -enum FileActionTarget { - FILE_ACTION_TARGET_NONE, - FILE_ACTION_TARGET_CONTEXT = 1, - FILE_ACTION_TARGET_LOCATION = 1 << 1, - FILE_ACTION_TARGET_TOOLBAR = 1 << 2 -}; - - -class FileActionObject { -public: - explicit FileActionObject(); - - explicit FileActionObject(GKeyFile* kf); - - virtual ~FileActionObject(); - - void setId(const char* _id) { - id = CStrPtr{g_strdup(_id)}; - } - - static bool is_plural_exec(const char* exec); - - static std::string expand_str(const char* templ, const FileInfoList& files, bool for_display = false, std::shared_ptr first_file = nullptr); - - FileActionType type; - CStrPtr id; - CStrPtr name; - CStrPtr tooltip; - CStrPtr icon; - CStrPtr desc; - bool enabled; - bool hidden; - CStrPtr suggested_shortcut; - std::unique_ptr condition; - - // values cached during menu generation - bool has_parent; -}; - - -class FileAction: public FileActionObject { -public: - - FileAction(GKeyFile* kf); - - std::shared_ptr match(const FileInfoList& files) const; - - int target; // bitwise or of FileActionTarget - CStrPtr toolbar_label; - - // FIXME: currently we don't support dynamic profiles - std::vector> profiles; -}; - - -class FileActionMenu : public FileActionObject { -public: - - FileActionMenu(GKeyFile* kf); - - bool match(const FileInfoList &files) const; - - // called during menu generation - void cache_children(const FileInfoList &files, const char** items_list); - - CStrArrayPtr items_list; - - // values cached during menu generation - std::vector> cached_children; -}; - - -class FileActionItem { -public: - - static std::shared_ptr fromActionObject(std::shared_ptr action_obj, const FileInfoList &files); - - FileActionItem(std::shared_ptr _action, std::shared_ptr _profile, const FileInfoList& files); - - FileActionItem(std::shared_ptr menu, const FileInfoList& files); - - FileActionItem(std::shared_ptr _action, const FileInfoList& files); - - const std::string& get_name() const { - return name; - } - - const std::string& get_desc() const { - return desc; - } - - const std::string& get_icon() const { - return icon; - } - - const char* get_id() const { - return action->id.get(); - } - - FileActionTarget get_target() const { - if(action->type == FileActionType::ACTION) { - return FileActionTarget(static_cast(action.get())->target); - } - return FILE_ACTION_TARGET_CONTEXT; - } - - bool is_menu() const { - return (action->type == FileActionType::MENU); - } - - bool is_action() const { - return (action->type == FileActionType::ACTION); - } - - bool launch(GAppLaunchContext *ctx, const FileInfoList &files, CStrPtr &output) const; - - const std::vector>& get_sub_items() const { - return children; - } - - static bool compare_items(std::shared_ptr a, std::shared_ptr b); - static std::vector> get_actions_for_files(const FileInfoList& files); - - std::string name; - std::string desc; - std::string icon; - std::shared_ptr action; - std::shared_ptr profile; // only used by action item - std::vector> children; // only used by menu -}; - -typedef std::vector> FileActionItemList; - -} // namespace Fm - - -#endif // FILEACTION_H diff --git a/src/customactions/fileactioncondition.cpp b/src/customactions/fileactioncondition.cpp deleted file mode 100644 index 55ee2e4..0000000 --- a/src/customactions/fileactioncondition.cpp +++ /dev/null @@ -1,508 +0,0 @@ -#include "fileactioncondition.h" -#include "fileaction.h" -#include - - -using namespace std; - -namespace Fm { - -FileActionCondition::FileActionCondition(GKeyFile *kf, const char* group) { - only_show_in = CStrArrayPtr{g_key_file_get_string_list(kf, group, "OnlyShowIn", nullptr, nullptr)}; - not_show_in = CStrArrayPtr{g_key_file_get_string_list(kf, group, "NotShowIn", nullptr, nullptr)}; - try_exec = CStrPtr{g_key_file_get_string(kf, group, "TryExec", nullptr)}; - show_if_registered = CStrPtr{g_key_file_get_string(kf, group, "ShowIfRegistered", nullptr)}; - show_if_true = CStrPtr{g_key_file_get_string(kf, group, "ShowIfTrue", nullptr)}; - show_if_running = CStrPtr{g_key_file_get_string(kf, group, "ShowIfRunning", nullptr)}; - mime_types = CStrArrayPtr{g_key_file_get_string_list(kf, group, "MimeTypes", nullptr, nullptr)}; - base_names = CStrArrayPtr{g_key_file_get_string_list(kf, group, "Basenames", nullptr, nullptr)}; - match_case = g_key_file_get_boolean(kf, group, "Matchcase", nullptr); - - CStrPtr selection_count_str{g_key_file_get_string(kf, group, "SelectionCount", nullptr)}; - if(selection_count_str != nullptr) { - switch(selection_count_str[0]) { - case '<': - case '>': - case '=': - selection_count_cmp = selection_count_str[0]; - selection_count = atoi(selection_count_str.get() + 1); - break; - default: - selection_count_cmp = '>'; - selection_count = 0; - break; - } - } - else { - selection_count_cmp = '>'; - selection_count = 0; - } - - schemes = CStrArrayPtr{g_key_file_get_string_list(kf, group, "Schemes", nullptr, nullptr)}; - folders = CStrArrayPtr{g_key_file_get_string_list(kf, group, "Folders", nullptr, nullptr)}; - auto caps = CStrArrayPtr{g_key_file_get_string_list(kf, group, "Capabilities", nullptr, nullptr)}; - - // FIXME: implement Capabilities support - -} - -bool FileActionCondition::match_try_exec(const FileInfoList& files) { - if(try_exec != nullptr) { - // stdout.printf(" TryExec: %s\n", try_exec); - CStrPtr exec_path{g_find_program_in_path(FileActionObject::expand_str(try_exec.get(), files).c_str())}; - if(!g_file_test(exec_path.get(), G_FILE_TEST_IS_EXECUTABLE)) { - return false; - } - } - return true; -} - -bool FileActionCondition::match_show_if_registered(const FileInfoList& files) { - if(show_if_registered != nullptr) { - // stdout.printf(" ShowIfRegistered: %s\n", show_if_registered); - auto service = FileActionObject::expand_str(show_if_registered.get(), files); - // References: - // http://people.freedesktop.org/~david/eggdbus-20091014/eggdbus-interface-org.freedesktop.DBus.html#eggdbus-method-org.freedesktop.DBus.NameHasOwner - // glib source code: gio/tests/gdbus-names.c - auto con = g_bus_get_sync(G_BUS_TYPE_SESSION, nullptr, nullptr); - auto result = g_dbus_connection_call_sync(con, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "NameHasOwner", - g_variant_new("(s)", service.c_str()), - g_variant_type_new("(b)"), - G_DBUS_CALL_FLAGS_NONE, - -1, nullptr, nullptr); - bool name_has_owner; - g_variant_get(result, "(b)", &name_has_owner); - g_variant_unref(result); - // stdout.printf("check if service: %s is in use: %d\n", service, (int)name_has_owner); - if(!name_has_owner) { - return false; - } - } - return true; -} - -bool FileActionCondition::match_show_if_true(const FileInfoList& files) { - if(show_if_true != nullptr) { - auto cmd = FileActionObject::expand_str(show_if_true.get(), files); - int exit_status; - // FIXME: Process.spawn cannot handle shell commands. Use Posix.system() instead. - //if(!Process.spawn_command_line_sync(cmd, nullptr, nullptr, out exit_status) - // || exit_status != 0) - // return false; - exit_status = system(cmd.c_str()); - if(exit_status != 0) { - return false; - } - } - return true; -} - -bool FileActionCondition::match_show_if_running(const FileInfoList& files) { - if(show_if_running != nullptr) { - auto process_name = FileActionObject::expand_str(show_if_running.get(), files); - CStrPtr pgrep{g_find_program_in_path("pgrep")}; - bool running = false; - // pgrep is not fully portable, but we don't have better options here - if(pgrep != nullptr) { - int exit_status; - // cmd = "$pgrep -x '$process_name'" - string cmd = pgrep.get(); - cmd += " -x \'"; - cmd += process_name; - cmd += "\'"; - if(g_spawn_command_line_sync(cmd.c_str(), nullptr, nullptr, &exit_status, nullptr)) { - if(exit_status == 0) { - running = true; - } - } - } - if(!running) { - return false; - } - } - return true; -} - -bool FileActionCondition::match_mime_type(const FileInfoList& files, const char* type, bool negated) { - // stdout.printf("match_mime_type: %s, neg: %d\n", type, (int)negated); - - if(strcmp(type, "all/all") == 0 || strcmp(type, "*") == 0) { - return negated ? false : true; - } - else if(strcmp(type, "all/allfiles") == 0) { - // see if all fileinfos are files - if(negated) { // all fileinfos should not be files - for(auto& fi: files) { - if(!fi->isDir()) { // at least 1 of the fileinfos is a file. - return false; - } - } - } - else { // all fileinfos should be files - for(auto& fi: files) { - if(fi->isDir()) { // at least 1 of the fileinfos is a file. - return false; - } - } - } - } - else if(g_str_has_suffix(type, "/*")) { - // check if all are subtypes of allowed_type - string prefix{type}; - prefix.erase(prefix.length() - 1); // remove the last char - if(negated) { // all files should not have the prefix - for(auto& fi: files) { - if(g_str_has_prefix(fi->mimeType()->name(), prefix.c_str())) { - return false; - } - } - } - else { // all files should have the prefix - for(auto& fi: files) { - if(!g_str_has_prefix(fi->mimeType()->name(), prefix.c_str())) { - return false; - } - } - } - } - else { - if(negated) { // all files should not be of the type - for(auto& fi: files) { - if(strcmp(fi->mimeType()->name(),type) == 0) { - // if(ContentType.is_a(type, fi.get_mime_type().get_type())) { - return false; - } - } - } - else { // all files should be of the type - for(auto& fi: files) { - // stdout.printf("get_type: %s, type: %s\n", fi.get_mime_type().get_type(), type); - if(strcmp(fi->mimeType()->name(),type) != 0) { - // if(!ContentType.is_a(type, fi.get_mime_type().get_type())) { - return false; - } - } - } - } - return true; -} - -bool FileActionCondition::match_mime_types(const FileInfoList& files) { - if(mime_types != nullptr) { - bool allowed = false; - // FIXME: this is inefficient, but easier to implement - // check if all of the mime_types are allowed - for(auto mime_type = mime_types.get(); *mime_type; ++mime_type) { - const char* allowed_type = *mime_type; - const char* type; - bool negated; - if(allowed_type[0] == '!') { - type = allowed_type + 1; - negated = true; - } - else { - type = allowed_type; - negated = false; - } - - if(negated) { // negated mime_type rules are ANDed - bool type_is_allowed = match_mime_type(files, type, negated); - if(!type_is_allowed) { // so any mismatch is not allowed - return false; - } - } - else { // other mime_type rules are ORed - // matching any one of the mime_type is enough - if(!allowed) { // if no rule is matched yet - allowed = match_mime_type(files, type, false); - } - } - } - return allowed; - } - return true; -} - -bool FileActionCondition::match_base_name(const FileInfoList& files, const char* base_name, bool negated) { - // see if all files has the base_name - // FIXME: this is inefficient, some optimization is needed later - GPatternSpec* pattern; - if(match_case) { - pattern = g_pattern_spec_new(base_name); - } - else { - CStrPtr case_fold{g_utf8_casefold(base_name, -1)}; - pattern = g_pattern_spec_new(case_fold.get()); // FIXME: is this correct? - } - for(auto& fi: files) { - const char* name = fi->name().c_str(); - if(match_case) { - if(g_pattern_match_string(pattern, name)) { - // at least 1 file has the base_name - if(negated) { - return false; - } - } - else { - // at least 1 file does not has the scheme - if(!negated) { - return false; - } - } - } - else { - CStrPtr case_fold{g_utf8_casefold(name, -1)}; - if(g_pattern_match_string(pattern, case_fold.get())) { - // at least 1 file has the base_name - if(negated) { - return false; - } - } - else { - // at least 1 file does not has the scheme - if(!negated) { - return false; - } - } - } - } - return true; -} - -bool FileActionCondition::match_base_names(const FileInfoList& files) { - if(base_names != nullptr) { - bool allowed = false; - // FIXME: this is inefficient, but easier to implement - // check if all of the base_names are allowed - for(auto it = base_names.get(); *it; ++it) { - auto allowed_name = *it; - const char* name; - bool negated; - if(allowed_name[0] == '!') { - name = allowed_name + 1; - negated = true; - } - else { - name = allowed_name; - negated = false; - } - - if(negated) { // negated base_name rules are ANDed - bool name_is_allowed = match_base_name(files, name, negated); - if(!name_is_allowed) { // so any mismatch is not allowed - return false; - } - } - else { // other base_name rules are ORed - // matching any one of the base_name is enough - if(!allowed) { // if no rule is matched yet - allowed = match_base_name(files, name, false); - } - } - } - return allowed; - } - return true; -} - -bool FileActionCondition::match_scheme(const FileInfoList& files, const char* scheme, bool negated) { - // FIXME: this is inefficient, some optimization is needed later - // see if all files has the scheme - for(auto& fi: files) { - if(fi->path().hasUriScheme(scheme)) { - // at least 1 file has the scheme - if(negated) { - return false; - } - } - else { - // at least 1 file does not has the scheme - if(!negated) { - return false; - } - } - } - return true; -} - -bool FileActionCondition::match_schemes(const FileInfoList& files) { - if(schemes != nullptr) { - bool allowed = false; - // FIXME: this is inefficient, but easier to implement - // check if all of the schemes are allowed - for(auto it = schemes.get(); *it; ++it) { - auto allowed_scheme = *it; - const char* scheme; - bool negated; - if(allowed_scheme[0] == '!') { - scheme = allowed_scheme + 1; - negated = true; - } - else { - scheme = allowed_scheme; - negated = false; - } - - if(negated) { // negated scheme rules are ANDed - bool scheme_is_allowed = match_scheme(files, scheme, negated); - if(!scheme_is_allowed) { // so any mismatch is not allowed - return false; - } - } - else { // other scheme rules are ORed - // matching any one of the scheme is enough - if(!allowed) { // if no rule is matched yet - allowed = match_scheme(files, scheme, false); - } - } - } - return allowed; - } - return true; -} - -bool FileActionCondition::match_folder(const FileInfoList& files, const char* folder, bool negated) { - // trailing /* should always be implied. - // FIXME: this is inefficient, some optimization is needed later - GPatternSpec* pattern; - if(g_str_has_suffix(folder, "/*")) { - pattern = g_pattern_spec_new(folder); - } - else { - auto pat_str = g_str_has_suffix(folder, "/") ? string(folder) + "*" // be tolerant - : string(folder) + "/*"; - pattern = g_pattern_spec_new(pat_str.c_str()); - } - for(auto& fi: files) { - auto dirname = fi->isDir() ? fi->path().toString() // also match "folder" itself - : fi->dirPath().toString(); - // Since the pattern ends with "/*", if the directory path is equal to "folder", - // it should end with "/" to be found as a match. Adding "/" is always harmless. - auto path_str = string(dirname.get()) + "/"; - if(g_pattern_match_string(pattern, path_str.c_str())) { // at least 1 file is in the folder - if(negated) { - return false; - } - } - else { - if(!negated) { - return false; - } - } - } - return true; -} - -bool FileActionCondition::match_folders(const FileInfoList& files) { - if(folders != nullptr) { - bool allowed = false; - // FIXME: this is inefficient, but easier to implement - // check if all of the schemes are allowed - for(auto it = folders.get(); *it; ++it) { - auto allowed_folder = *it; - const char* folder; - bool negated; - if(allowed_folder[0] == '!') { - folder = allowed_folder + 1; - negated = true; - } - else { - folder = allowed_folder; - negated = false; - } - - if(negated) { // negated folder rules are ANDed - bool folder_is_allowed = match_folder(files, folder, negated); - if(!folder_is_allowed) { // so any mismatch is not allowed - return false; - } - } - else { // other folder rules are ORed - // matching any one of the folder is enough - if(!allowed) { // if no rule is matched yet - allowed = match_folder(files, folder, false); - } - } - } - return allowed; - } - return true; -} - -bool FileActionCondition::match_selection_count(const FileInfoList& files) { - const int n_files = files.size(); - switch(selection_count_cmp) { - case '<': - if(n_files >= selection_count) { - return false; - } - break; - case '=': - if(n_files != selection_count) { - return false; - } - break; - case '>': - if(n_files <= selection_count) { - return false; - } - break; - } - return true; -} - -bool FileActionCondition::match_capabilities(const FileInfoList& /*files*/) { - // TODO - return true; -} - -bool FileActionCondition::match(const FileInfoList& files) { - // all of the condition are combined with AND - // So, if any one of the conditions is not matched, we quit. - - // TODO: OnlyShowIn, NotShowIn - if(!match_try_exec(files)) { - return false; - } - - if(!match_mime_types(files)) { - return false; - } - if(!match_base_names(files)) { - return false; - } - if(!match_selection_count(files)) { - return false; - } - if(!match_schemes(files)) { - return false; - } - if(!match_folders(files)) { - return false; - } - // TODO: Capabilities - // currently, due to limitations of Fm.FileInfo, this cannot - // be implemanted correctly. - if(!match_capabilities(files)) { - return false; - } - - if(!match_show_if_registered(files)) { - return false; - } - if(!match_show_if_true(files)) { - return false; - } - if(!match_show_if_running(files)) { - return false; - } - - return true; -} - - -} // namespace Fm diff --git a/src/customactions/fileactioncondition.h b/src/customactions/fileactioncondition.h deleted file mode 100644 index 5390cdd..0000000 --- a/src/customactions/fileactioncondition.h +++ /dev/null @@ -1,123 +0,0 @@ -#ifndef FILEACTIONCONDITION_H -#define FILEACTIONCONDITION_H - -#include -#include "../core/gioptrs.h" -#include "../core/fileinfo.h" - -namespace Fm { - -// FIXME: we can use getgroups() to get groups of current process -// then, call stat() and stat.st_gid to handle capabilities -// in this way, we don't have to call euidaccess - -enum class FileActionCapability { - OWNER = 0, - READABLE = 1 << 1, - WRITABLE = 1 << 2, - EXECUTABLE = 1 << 3, - LOCAL = 1 << 4 -}; - - -class FileActionCondition { -public: - explicit FileActionCondition(GKeyFile* kf, const char* group); - -#if 0 - bool match_base_name_(const FileInfoList& files, const char* allowed_base_name) { - // all files should match the base_name pattern. - bool allowed = true; - if(allowed_base_name.index_of_char('*') >= 0) { - string allowed_base_name_ci; - if(!match_case) { - allowed_base_name_ci = allowed_base_name.casefold(); // FIXME: is this ok? - allowed_base_name = allowed_base_name_ci; - } - var pattern = new PatternSpec(allowed_base_name); - foreach(unowned FileInfo fi in files) { - unowned string name = fi.get_name(); - if(match_case) { - if(!pattern.match_string(name)) { - allowed = false; - break; - } - } - else { - if(!pattern.match_string(name.casefold())) { - allowed = false; - break; - } - } - } - } - else { - foreach(unowned FileInfo fi in files) { - unowned string name = fi.get_name(); - if(match_case) { - if(allowed_base_name != name) { - allowed = false; - break; - } - } - else { - if(allowed_base_name.collate(name) != 0) { - allowed = false; - break; - } - } - } - } - return allowed; - } -#endif - - bool match_try_exec(const FileInfoList& files); - - bool match_show_if_registered(const FileInfoList& files); - - bool match_show_if_true(const FileInfoList& files); - - bool match_show_if_running(const FileInfoList& files); - - bool match_mime_type(const FileInfoList& files, const char* type, bool negated); - - bool match_mime_types(const FileInfoList& files); - - bool match_base_name(const FileInfoList& files, const char* base_name, bool negated); - - bool match_base_names(const FileInfoList& files); - - static bool match_scheme(const FileInfoList& files, const char* scheme, bool negated); - - bool match_schemes(const FileInfoList& files); - - static bool match_folder(const FileInfoList& files, const char* folder, bool negated); - - bool match_folders(const FileInfoList& files); - - bool match_selection_count(const FileInfoList &files); - - bool match_capabilities(const FileInfoList& files); - - bool match(const FileInfoList& files); - - CStrArrayPtr only_show_in; - CStrArrayPtr not_show_in; - CStrPtr try_exec; - CStrPtr show_if_registered; - CStrPtr show_if_true; - CStrPtr show_if_running; - CStrArrayPtr mime_types; - CStrArrayPtr base_names; - bool match_case; - char selection_count_cmp; - int selection_count; - CStrArrayPtr schemes; - CStrArrayPtr folders; - FileActionCapability capabilities; -}; - -} - -#endif // FILEACTIONCONDITION_H diff --git a/src/customactions/fileactionprofile.cpp b/src/customactions/fileactionprofile.cpp deleted file mode 100644 index 0150b1c..0000000 --- a/src/customactions/fileactionprofile.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include "fileactionprofile.h" -#include "fileaction.h" -#include - -using namespace std; - -namespace Fm { - -FileActionProfile::FileActionProfile(GKeyFile *kf, const char* profile_name) { - id = profile_name; - std::string group_name = "X-Action-Profile " + id; - name = CStrPtr{g_key_file_get_string(kf, group_name.c_str(), "Name", nullptr)}; - exec = CStrPtr{g_key_file_get_string(kf, group_name.c_str(), "Exec", nullptr)}; - // stdout.printf("id: %s, Exec: %s\n", id, exec); - - path = CStrPtr{g_key_file_get_string(kf, group_name.c_str(), "Path", nullptr)}; - auto s = CStrPtr{g_key_file_get_string(kf, group_name.c_str(), "ExecutionMode", nullptr)}; - if(s) { - if(strcmp(s.get(), "Normal") == 0) { - exec_mode = FileActionExecMode::NORMAL; - } - else if(strcmp(s.get(), "Terminal") == 0) { - exec_mode = FileActionExecMode::TERMINAL; - } - else if(strcmp(s.get(), "Embedded") == 0) { - exec_mode = FileActionExecMode::EMBEDDED; - } - else if(strcmp(s.get(), "DisplayOutput") == 0) { - exec_mode = FileActionExecMode::DISPLAY_OUTPUT; - } - else { - exec_mode = FileActionExecMode::NORMAL; - } - } - else { - exec_mode = FileActionExecMode::NORMAL; - } - - startup_notify = g_key_file_get_boolean(kf, group_name.c_str(), "StartupNotify", nullptr); - startup_wm_class = CStrPtr{g_key_file_get_string(kf, group_name.c_str(), "StartupWMClass", nullptr)}; - exec_as = CStrPtr{g_key_file_get_string(kf, group_name.c_str(), "ExecuteAs", nullptr)}; - - condition = make_shared(kf, group_name.c_str()); -} - - -bool FileActionProfile::launch_once(GAppLaunchContext* /*ctx*/, std::shared_ptr first_file, const FileInfoList& files, CStrPtr& output) { - if(exec == nullptr) { - return false; - } - auto exec_cmd = FileActionObject::expand_str(exec.get(), files, false, first_file); - bool ret = false; - if(exec_mode == FileActionExecMode::DISPLAY_OUTPUT) { - int exit_status; - char* output_buf = nullptr; - ret = g_spawn_command_line_sync(exec_cmd.c_str(), &output_buf, nullptr, &exit_status, nullptr); - if(ret) { - ret = (exit_status == 0); - } - output = CStrPtr{output_buf}; - } - else { - /* - AppInfoCreateFlags flags = AppInfoCreateFlags.NONE; - if(startup_notify) - flags |= AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION; - if(exec_mode == FileActionExecMode::TERMINAL || - exec_mode == FileActionExecMode::EMBEDDED) - flags |= AppInfoCreateFlags.NEEDS_TERMINAL; - GLib.AppInfo app = Fm.AppInfo.create_from_commandline(exec, nullptr, flags); - stdout.printf("Execute command line: %s\n\n", exec); - ret = app.launch(nullptr, ctx); - */ - - // NOTE: we cannot use GAppInfo here since GAppInfo does - // command line parsing which involving %u, %f, and other - // code defined in desktop entry spec. - // This may conflict with DES EMA parameters. - // FIXME: so how to handle this cleaner? - // Maybe we should leave all %% alone and don't translate - // them to %. Then GAppInfo will translate them to %, not - // codes specified in DES. - ret = g_spawn_command_line_async(exec_cmd.c_str(), nullptr); - } - return ret; -} - - -bool FileActionProfile::launch(GAppLaunchContext* ctx, const FileInfoList& files, CStrPtr& output) { - bool plural_form = FileActionObject::is_plural_exec(exec.get()); - bool ret; - if(plural_form) { // plural form command, handle all files at a time - ret = launch_once(ctx, files.front(), files, output); - } - else { // singular form command, run once for each file - GString* all_output = g_string_sized_new(1024); - bool show_output = false; - for(auto& fi: files) { - CStrPtr one_output; - launch_once(ctx, fi, files, one_output); - if(one_output) { - show_output = true; - // FIXME: how to handle multiple output std::strings properly? - g_string_append(all_output, one_output.get()); - g_string_append(all_output, "\n"); - } - } - if(show_output) { - output = CStrPtr{g_string_free(all_output, false)}; - } - else { - g_string_free(all_output, true); - } - ret = true; - } - return ret; -} - -bool FileActionProfile::match(FileInfoList files) { - // stdout.printf(" match profile: %s\n", id); - return condition->match(files); -} - -} diff --git a/src/customactions/fileactionprofile.h b/src/customactions/fileactionprofile.h deleted file mode 100644 index 18b959f..0000000 --- a/src/customactions/fileactionprofile.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef FILEACTIONPROFILE_H -#define FILEACTIONPROFILE_H - - -#include -#include -#include - -#include "../core/fileinfo.h" -#include "fileactioncondition.h" - -namespace Fm { - -enum class FileActionExecMode { - NORMAL, - TERMINAL, - EMBEDDED, - DISPLAY_OUTPUT -}; - -class FileActionProfile { -public: - explicit FileActionProfile(GKeyFile* kf, const char* profile_name); - - bool launch_once(GAppLaunchContext* ctx, std::shared_ptr first_file, const FileInfoList& files, CStrPtr& output); - - bool launch(GAppLaunchContext* ctx, const FileInfoList& files, CStrPtr& output); - - bool match(FileInfoList files); - - std::string id; - CStrPtr name; - CStrPtr exec; - CStrPtr path; - FileActionExecMode exec_mode; - bool startup_notify; - CStrPtr startup_wm_class; - CStrPtr exec_as; - - std::shared_ptr condition; -}; - -} // namespace Fm - -#endif // FILEACTIONPROFILE_H diff --git a/src/dirtreemodel.cpp b/src/dirtreemodel.cpp deleted file mode 100644 index c6d030f..0000000 --- a/src/dirtreemodel.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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 "dirtreemodel.h" -#include "dirtreemodelitem.h" -#include -#include "core/fileinfojob.h" - -namespace Fm { - -DirTreeModel::DirTreeModel(QObject* parent): - QAbstractItemModel(parent), - showHidden_(false) { -} - -DirTreeModel::~DirTreeModel() { -} - -void DirTreeModel::addRoots(Fm::FilePathList rootPaths) { - auto job = new Fm::FileInfoJob{std::move(rootPaths)}; - job->setAutoDelete(true); - connect(job, &Fm::FileInfoJob::finished, this, &DirTreeModel::onFileInfoJobFinished, Qt::BlockingQueuedConnection); - job->runAsync(); -} - -void DirTreeModel::onFileInfoJobFinished() { - auto job = static_cast(sender()); - for(auto file: job->files()) { - addRoot(std::move(file)); - } -} - -// QAbstractItemModel implementation - -Qt::ItemFlags DirTreeModel::flags(const QModelIndex& index) const { - DirTreeModelItem* item = itemFromIndex(index); - if(item && item->isPlaceHolder()) { - return Qt::ItemIsEnabled; - } - return QAbstractItemModel::flags(index); -} - -QVariant DirTreeModel::data(const QModelIndex& index, int role) const { - if(!index.isValid() || index.column() > 1) { - return QVariant(); - } - DirTreeModelItem* item = itemFromIndex(index); - if(item) { - auto info = item->fileInfo_; - switch(role) { - case Qt::ToolTipRole: - return QVariant(item->displayName_); - case Qt::DisplayRole: - return QVariant(item->displayName_); - case Qt::DecorationRole: - return QVariant(item->icon_); - case FileInfoRole: { - QVariant v; - v.setValue(info); - return v; - } - } - } - return QVariant(); -} - -int DirTreeModel::columnCount(const QModelIndex& /*parent*/) const { - return 1; -} - -int DirTreeModel::rowCount(const QModelIndex& parent) const { - if(!parent.isValid()) { - return rootItems_.size(); - } - DirTreeModelItem* item = itemFromIndex(parent); - if(item) { - return item->children_.size(); - } - return 0; -} - -QModelIndex DirTreeModel::parent(const QModelIndex& child) const { - DirTreeModelItem* item = itemFromIndex(child); - if(item && item->parent_) { - item = item->parent_; // go to parent item - if(item) { - const auto& items = item->parent_ ? item->parent_->children_ : rootItems_; - auto it = std::find(items.cbegin(), items.cend(), item); - if(it != items.cend()) { - int row = it - items.cbegin(); - return createIndex(row, 0, (void*)item); - } - } - } - return QModelIndex(); -} - -QModelIndex DirTreeModel::index(int row, int column, const QModelIndex& parent) const { - if(row >= 0 && column >= 0 && column == 0) { - if(!parent.isValid()) { // root items - if(static_cast(row) < rootItems_.size()) { - const DirTreeModelItem* item = rootItems_.at(row); - return createIndex(row, column, (void*)item); - } - } - else { // child items - DirTreeModelItem* parentItem = itemFromIndex(parent); - if(static_cast(row) < parentItem->children_.size()) { - const DirTreeModelItem* item = parentItem->children_.at(row); - return createIndex(row, column, (void*)item); - } - } - } - return QModelIndex(); // invalid index -} - -bool DirTreeModel::hasChildren(const QModelIndex& parent) const { - DirTreeModelItem* item = itemFromIndex(parent); - return item ? !item->isPlaceHolder() : true; -} - -QModelIndex DirTreeModel::indexFromItem(DirTreeModelItem* item) const { - Q_ASSERT(item); - const auto& items = item->parent_ ? item->parent_->children_ : rootItems_; - auto it = std::find(items.cbegin(), items.cend(), item); - if(it != items.cend()) { - int row = it - items.cbegin(); - return createIndex(row, 0, (void*)item); - } - return QModelIndex(); -} - -// public APIs -QModelIndex DirTreeModel::addRoot(std::shared_ptr root) { - DirTreeModelItem* item = new DirTreeModelItem(std::move(root), this); - int row = rootItems_.size(); - beginInsertRows(QModelIndex(), row, row); - rootItems_.push_back(item); - // add_place_holder_child_item(model, item_l, nullptr, FALSE); - endInsertRows(); - return createIndex(row, 0, (void*)item); -} - -DirTreeModelItem* DirTreeModel::itemFromIndex(const QModelIndex& index) const { - return reinterpret_cast(index.internalPointer()); -} - -QModelIndex DirTreeModel::indexFromPath(const Fm::FilePath &path) const { - DirTreeModelItem* item = itemFromPath(path); - return item ? item->index() : QModelIndex(); -} - -DirTreeModelItem* DirTreeModel::itemFromPath(const Fm::FilePath &path) const { - for(DirTreeModelItem* const item : qAsConst(rootItems_)) { - if(item->fileInfo_ && path == item->fileInfo_->path()) { - return item; - } - else { - DirTreeModelItem* child = item->childFromPath(path, true); - if(child) { - return child; - } - } - } - return nullptr; -} - - -void DirTreeModel::loadRow(const QModelIndex& index) { - DirTreeModelItem* item = itemFromIndex(index); - Q_ASSERT(item); - if(item && !item->isPlaceHolder()) { - item->loadFolder(); - } -} - -void DirTreeModel::unloadRow(const QModelIndex& index) { - DirTreeModelItem* item = itemFromIndex(index); - if(item && !item->isPlaceHolder()) { - item->unloadFolder(); - } -} - -bool DirTreeModel::isLoaded(const QModelIndex& index) { - DirTreeModelItem* item = itemFromIndex(index); - return item ? item->loaded_ : false; -} - -QIcon DirTreeModel::icon(const QModelIndex& index) { - DirTreeModelItem* item = itemFromIndex(index); - return item ? item->icon_ : QIcon(); -} - -std::shared_ptr DirTreeModel::fileInfo(const QModelIndex& index) { - DirTreeModelItem* item = itemFromIndex(index); - return item ? item->fileInfo_ : nullptr; -} - -Fm::FilePath DirTreeModel::filePath(const QModelIndex& index) { - DirTreeModelItem* item = itemFromIndex(index); - return (item && item->fileInfo_) ? item->fileInfo_->path() : Fm::FilePath{}; -} - -QString DirTreeModel::dispName(const QModelIndex& index) { - DirTreeModelItem* item = itemFromIndex(index); - return item ? item->displayName_ : QString(); -} - -void DirTreeModel::setShowHidden(bool show_hidden) { - showHidden_ = show_hidden; - for(DirTreeModelItem* const item : qAsConst(rootItems_)) { - item->setShowHidden(show_hidden); - } -} - - -} // namespace Fm diff --git a/src/dirtreemodel.h b/src/dirtreemodel.h deleted file mode 100644 index c254713..0000000 --- a/src/dirtreemodel.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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_DIRTREEMODEL_H -#define FM_DIRTREEMODEL_H - -#include "libfmqtglobals.h" -#include -#include -#include -#include -#include -#include -#include - -#include "core/fileinfo.h" -#include "core/filepath.h" - -namespace Fm { - -class DirTreeModelItem; -class DirTreeView; - -class LIBFM_QT_API DirTreeModel : public QAbstractItemModel { - Q_OBJECT - -public: - friend class DirTreeModelItem; // allow direct access of private members in DirTreeModelItem - friend class DirTreeView; // allow direct access of private members in DirTreeView - - enum Role { - FileInfoRole = Qt::UserRole - }; - - explicit DirTreeModel(QObject* parent); - ~DirTreeModel(); - - void addRoots(Fm::FilePathList rootPaths); - - void loadRow(const QModelIndex& index); - void unloadRow(const QModelIndex& index); - - bool isLoaded(const QModelIndex& index); - QIcon icon(const QModelIndex& index); - std::shared_ptr fileInfo(const QModelIndex& index); - Fm::FilePath filePath(const QModelIndex& index); - QString dispName(const QModelIndex& index); - - void setShowHidden(bool show_hidden); - bool showHidden() const { - return showHidden_; - } - - QModelIndex indexFromPath(const Fm::FilePath& path) const; - - virtual Qt::ItemFlags flags(const QModelIndex& index) const; - virtual QVariant data(const QModelIndex& index, int role) const; - virtual int columnCount(const QModelIndex& parent) const; - virtual int rowCount(const QModelIndex& parent) const; - virtual QModelIndex parent(const QModelIndex& child) const; - virtual QModelIndex index(int row, int column, const QModelIndex& parent) const; - virtual bool hasChildren(const QModelIndex& parent = QModelIndex()) const; - -Q_SIGNALS: - void rowLoaded(const QModelIndex& index); - -private Q_SLOTS: - void onFileInfoJobFinished(); - -private: - QModelIndex addRoot(std::shared_ptr root); - - DirTreeModelItem* itemFromPath(const Fm::FilePath& path) const; - DirTreeModelItem* itemFromIndex(const QModelIndex& index) const; - QModelIndex indexFromItem(DirTreeModelItem* item) const; - -private: - bool showHidden_; - std::vector rootItems_; -}; - -} - -#endif // FM_DIRTREEMODEL_H diff --git a/src/dirtreemodelitem.cpp b/src/dirtreemodelitem.cpp deleted file mode 100644 index 57ecf41..0000000 --- a/src/dirtreemodelitem.cpp +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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 "dirtreemodelitem.h" -#include "dirtreemodel.h" -#include - -namespace Fm { - -DirTreeModelItem::DirTreeModelItem(): - fileInfo_(nullptr), - folder_(nullptr), - expanded_(false), - loaded_(false), - parent_(nullptr), - placeHolderChild_(nullptr), - model_(nullptr), - queuedForDeletion_(false) { -} - -DirTreeModelItem::DirTreeModelItem(std::shared_ptr info, DirTreeModel* model, DirTreeModelItem* parent): - fileInfo_{std::move(info)}, - expanded_(false), - loaded_(false), - parent_(parent), - placeHolderChild_(nullptr), - model_(model), - queuedForDeletion_(false) { - - if(fileInfo_) { - displayName_ = fileInfo_->displayName(); - icon_ = fileInfo_->icon()->qicon(); - addPlaceHolderChild(); - } -} - -DirTreeModelItem::~DirTreeModelItem() { - freeFolder(); - // delete child items if needed - if(!children_.empty()) { - for(DirTreeModelItem* const item : qAsConst(children_)) { - delete item; - } - } - if(!hiddenChildren_.empty()) { - for(DirTreeModelItem* const item : qAsConst(hiddenChildren_)) { - delete item; - } - } - /*if(queuedForDeletion_) - qDebug() << "queued deletion done";*/ -} - -void DirTreeModelItem::freeFolder() { - if(folder_) { - QObject::disconnect(onFolderFinishLoadingConn_); - QObject::disconnect(onFolderFilesAddedConn_); - QObject::disconnect(onFolderFilesRemovedConn_); - QObject::disconnect(onFolderFilesChangedConn_); - folder_.reset(); - } -} - -void DirTreeModelItem::addPlaceHolderChild() { - placeHolderChild_ = new DirTreeModelItem(); - placeHolderChild_->parent_ = this; - placeHolderChild_->model_ = model_; - placeHolderChild_->displayName_ = DirTreeModel::tr("Loading..."); - children_.push_back(placeHolderChild_); -} - -void DirTreeModelItem::loadFolder() { - if(!expanded_) { - /* dynamically load content of the folder. */ - folder_ = Fm::Folder::fromPath(fileInfo_->path()); - /* g_debug("fm_dir_tree_model_load_row()"); */ - /* associate the data with loaded handler */ - - onFolderFinishLoadingConn_ = QObject::connect(folder_.get(), &Fm::Folder::finishLoading, model_, [=]() { - onFolderFinishLoading(); - }); - onFolderFilesAddedConn_ = QObject::connect(folder_.get(), &Fm::Folder::filesAdded, model_, [=](Fm::FileInfoList files) { - onFolderFilesAdded(files); - }); - onFolderFilesRemovedConn_ = QObject::connect(folder_.get(), &Fm::Folder::filesRemoved, model_, [=](Fm::FileInfoList files) { - onFolderFilesRemoved(files); - }); - onFolderFilesChangedConn_ = QObject::connect(folder_.get(), &Fm::Folder::filesChanged, model_, [=](std::vector& changes) { - onFolderFilesChanged(changes); - }); - - /* set 'expanded' flag beforehand as callback may check it */ - expanded_ = true; - /* if the folder is already loaded, call "loaded" handler ourselves */ - if(folder_->isLoaded()) { // already loaded - insertFiles(folder_->files()); - onFolderFinishLoading(); - } - } -} - -void DirTreeModelItem::unloadFolder() { - if(expanded_) { /* do some cleanup */ - /* remove all children, and replace them with a dummy child - * item to keep expander in the tree view around. */ - - // delete all visible child items - model_->beginRemoveRows(index(), 0, children_.size() - 1); - if(!children_.empty()) { - for(DirTreeModelItem* const item : qAsConst(children_)) { - delete item; - } - children_.clear(); - } - model_->endRemoveRows(); - - // remove hidden children - if(!hiddenChildren_.empty()) { - for(DirTreeModelItem* const item : qAsConst(hiddenChildren_)) { - delete item; - } - hiddenChildren_.clear(); - } - - /* now, we have no child since all child items are removed. - * So we add a place holder child item to keep the expander around. */ - addPlaceHolderChild(); - /* deactivate folder since it will be reactivated on expand */ - freeFolder(); - expanded_ = false; - loaded_ = false; - } -} - -QModelIndex DirTreeModelItem::index() { - Q_ASSERT(model_); - return model_->indexFromItem(this); -} - -/* Add file info to parent node to proper position. */ -DirTreeModelItem* DirTreeModelItem::insertFile(std::shared_ptr fi) { - // qDebug() << "insertFileInfo: " << fm_file_info_get_disp_name(fi); - DirTreeModelItem* item = new DirTreeModelItem(std::move(fi), model_); - insertItem(item); - return item; -} - -/* Add file info to parent node to proper position. */ -void DirTreeModelItem::insertFiles(Fm::FileInfoList files) { - if(children_.size() == 1 && placeHolderChild_) { - // the list is empty, add them all at once and do sort - if(!model_->showHidden()) { // need to separate visible and hidden items - // insert hidden files into the "hiddenChildren_" list and remove them from "files" list - // WARNING: "std::remove_if" shouldn't be used to work on the "removed" items because, as - // docs say, the elements between the returned and the end iterators are in an unspecified - // state and, as far as I (@tsujan) have tested, some of them announce themselves as null. - for(auto it = files.begin(); it != files.end();) { - auto file = *it; - if(file->isHidden()) { - hiddenChildren_.push_back(new DirTreeModelItem{std::move(file), model_}); - it = files.erase(it); - } - else { - ++it; - } - } - - } - // sort the remaining visible files by name - std::sort(files.begin(), files.end(), [](const std::shared_ptr& a, const std::shared_ptr& b) { - return QString::localeAwareCompare(a->displayName(), b->displayName()) < 0; - }); - // insert the files into the visible children list at once - model_->beginInsertRows(index(), 1, files.size() + 1); // the first item is the placeholder item, so we start from row 1 - for(auto& file: files) { - if(file->isDir()) { - DirTreeModelItem* newItem = new DirTreeModelItem(std::move(file), model_); - newItem->parent_ = this; - children_.push_back(newItem); - } - } - model_->endInsertRows(); - - // remove the place holder if a folder is added - if(children_.size() > 1) { - auto it = std::find(children_.cbegin(), children_.cend(), placeHolderChild_); - if(it != children_.cend()) { - auto pos = it - children_.cbegin(); - model_->beginRemoveRows(index(), pos, pos); - children_.erase(it); - delete placeHolderChild_; - model_->endRemoveRows(); - placeHolderChild_ = nullptr; - } - } - } - else { - // the list already contain some items, insert new items one by one so they can be sorted. - for(auto& file: files) { - if(file->isDir()) { - insertFile(std::move(file)); - } - } - } -} - -// find a good position to insert the new item -// FIXME: insert one item at a time is slow. Insert multiple items at once and then sort is faster. -int DirTreeModelItem::insertItem(DirTreeModelItem* newItem) { - if(!newItem->fileInfo_ || !newItem->fileInfo_->isDir()) { - // don't insert placeholders or non-directory files - return -1; - } - if(model_->showHidden() || !newItem->fileInfo_ || !newItem->fileInfo_->isHidden()) { - auto it = std::lower_bound(children_.cbegin(), children_.cend(), newItem, [=](const DirTreeModelItem* a, const DirTreeModelItem* b) { - if(Q_UNLIKELY(!a->fileInfo_)) { - return true; // this is a placeholder item which will be removed so the order doesn't matter. - } - if(Q_UNLIKELY(!b->fileInfo_)) { - return false; - } - return QString::localeAwareCompare(a->fileInfo_->displayName(), b->fileInfo_->displayName()) < 0; - }); - // inform the world that we're about to insert the item - auto position = it - children_.begin(); - model_->beginInsertRows(index(), position, position); - newItem->parent_ = this; - children_.insert(it, newItem); - model_->endInsertRows(); - return position; - } - else { // hidden folder - hiddenChildren_.push_back(newItem); - } - return -1; -} - - -// FmFolder signal handlers - -void DirTreeModelItem::onFolderFinishLoading() { - DirTreeModel* model = model_; - /* set 'loaded' flag beforehand as callback may check it */ - loaded_ = true; - QModelIndex idx = index(); - //qDebug() << "folder loaded"; - // remove the placeholder child if needed - // (a check for its existence is necessary; see insertItem) - if(placeHolderChild_) { - if(children_.size() == 1) { // we have no other child other than the place holder item, leave it - placeHolderChild_->displayName_ = DirTreeModel::tr(""); - QModelIndex placeHolderIndex = placeHolderChild_->index(); - // qDebug() << "placeHolderIndex: "<dataChanged(placeHolderIndex, placeHolderIndex); - } - else { - auto it = std::find(children_.cbegin(), children_.cend(), placeHolderChild_); - if(it != children_.cend()) { - auto pos = it - children_.cbegin(); - model->beginRemoveRows(idx, pos, pos); - children_.erase(it); - delete placeHolderChild_; - model->endRemoveRows(); - placeHolderChild_ = nullptr; - } - } - } - - Q_EMIT model->rowLoaded(idx); -} - -void DirTreeModelItem::onFolderFilesAdded(Fm::FileInfoList& files) { - insertFiles(files); -} - -void DirTreeModelItem::onFolderFilesRemoved(Fm::FileInfoList& files) { - DirTreeModel* model = model_; - - for(auto& fi: files) { - int pos; - DirTreeModelItem* child = childFromName(fi->name().c_str(), &pos); - if(child) { - // The item shouldn't be deleted now but after its row is removed from QTreeView; - // otherwise a freeze will happen when it has a child item (its row is expanded). - child->queuedForDeletion_ = true; - model->beginRemoveRows(index(), pos, pos); - children_.erase(children_.cbegin() + pos); - model->endRemoveRows(); - - } - } - - if(children_.empty()) { // no visible children, add a placeholder item to keep the row expanded - addPlaceHolderChild(); - placeHolderChild_->displayName_ = DirTreeModel::tr(""); - } -} - -void DirTreeModelItem::onFolderFilesChanged(std::vector &changes) { - DirTreeModel* model = model_; - for(auto& changePair: changes) { - int pos; - auto& changedFile = changePair.first; - DirTreeModelItem* child = childFromName(changedFile->name().c_str(), &pos); - if(child) { - QModelIndex childIndex = child->index(); - Q_EMIT model->dataChanged(childIndex, childIndex); - } - } -} - -DirTreeModelItem* DirTreeModelItem::childFromName(const char* utf8_name, int* pos) { - int i = 0; - for(const auto item : children_) { - if(item->fileInfo_ && item->fileInfo_->name() == utf8_name) { - if(pos) { - *pos = i; - } - return item; - } - ++i; - } - return nullptr; -} - -DirTreeModelItem* DirTreeModelItem::childFromPath(Fm::FilePath path, bool recursive) const { - Q_ASSERT(path != nullptr); - - for(DirTreeModelItem* const item : qAsConst(children_)) { - // if(item->fileInfo_) - // qDebug() << "child: " << QString::fromUtf8(fm_file_info_get_disp_name(item->fileInfo_)); - if(item->fileInfo_ && item->fileInfo_->path() == path) { - return item; - } - else if(recursive) { - DirTreeModelItem* child = item->childFromPath(std::move(path), true); - if(child) { - return child; - } - } - } - return nullptr; -} - -void DirTreeModelItem::setShowHidden(bool show) { - if(show) { - // move all hidden children to visible list - for(auto item: hiddenChildren_) { - insertItem(item); - } - hiddenChildren_.clear(); - // remove the placeholder if needed - if(children_.size() > 1) { - auto it = std::find(children_.cbegin(), children_.cend(), placeHolderChild_); - if(it != children_.cend()) { - auto pos = it - children_.cbegin(); - model_->beginRemoveRows(index(), pos, pos); - children_.erase(it); - delete placeHolderChild_; - model_->endRemoveRows(); - placeHolderChild_ = nullptr; - } - } - // recursively show children of children, etc. - for(auto item: children_) { - item->setShowHidden(true); - } - } - else { // hide hidden folders - QModelIndex _index = index(); - int pos = 0; - for(auto it = children_.begin(); it != children_.end(); ++pos) { - DirTreeModelItem* item = *it; - if(item->fileInfo_) { - if(item->fileInfo_->isHidden()) { // hidden folder - // remove from the model and add to the hiddenChildren_ list - model_->beginRemoveRows(_index, pos, pos); - it = children_.erase(it); - hiddenChildren_.push_back(item); - model_->endRemoveRows(); - } - else { // visible folder, recursively filter its children - item->setShowHidden(show); - ++it; - } - } - else { - ++it; - } - } - if(children_.empty()) { // no visible children, add a placeholder item to keep the row expanded - addPlaceHolderChild(); - placeHolderChild_->displayName_ = DirTreeModel::tr(""); - } - } -} - - - -} // namespace Fm diff --git a/src/dirtreemodelitem.h b/src/dirtreemodelitem.h deleted file mode 100644 index 9b6e8d0..0000000 --- a/src/dirtreemodelitem.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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_DIRTREEMODELITEM_H -#define FM_DIRTREEMODELITEM_H - -#include "libfmqtglobals.h" -#include -#include -#include -#include - -#include "core/fileinfo.h" -#include "core/folder.h" - -namespace Fm { - -class DirTreeModel; -class DirTreeView; - -class LIBFM_QT_API DirTreeModelItem { -public: - friend class DirTreeModel; // allow direct access of private members in DirTreeModel - friend class DirTreeView; // allow direct access of private members in DirTreeView - - explicit DirTreeModelItem(); - explicit DirTreeModelItem(std::shared_ptr info, DirTreeModel* model, DirTreeModelItem* parent = nullptr); - ~DirTreeModelItem(); - - void loadFolder(); - void unloadFolder(); - - inline bool isPlaceHolder() const { - return (fileInfo_ == nullptr); - } - - void setShowHidden(bool show); - - bool isQueuedForDeletion() { - return queuedForDeletion_; - } - - -private: - void freeFolder(); - void addPlaceHolderChild(); - DirTreeModelItem* childFromName(const char* utf8_name, int* pos); - DirTreeModelItem* childFromPath(Fm::FilePath path, bool recursive) const; - - DirTreeModelItem* insertFile(std::shared_ptr fi); - void insertFiles(Fm::FileInfoList files); - int insertItem(Fm::DirTreeModelItem* newItem); - QModelIndex index(); - - void onFolderFinishLoading(); - void onFolderFilesAdded(Fm::FileInfoList &files); - void onFolderFilesRemoved(Fm::FileInfoList &files); - void onFolderFilesChanged(std::vector& changes); - -private: - std::shared_ptr fileInfo_; - std::shared_ptr folder_; - QString displayName_ ; - QIcon icon_; - bool expanded_; - bool loaded_; - DirTreeModelItem* parent_; - DirTreeModelItem* placeHolderChild_; - std::vector children_; - std::vector hiddenChildren_; - DirTreeModel* model_; - bool queuedForDeletion_; - // signal connections - QMetaObject::Connection onFolderFinishLoadingConn_; - QMetaObject::Connection onFolderFilesAddedConn_; - QMetaObject::Connection onFolderFilesRemovedConn_; - QMetaObject::Connection onFolderFilesChangedConn_; -}; - -} - -#endif // FM_DIRTREEMODELITEM_H diff --git a/src/dirtreeview.cpp b/src/dirtreeview.cpp deleted file mode 100644 index 7230559..0000000 --- a/src/dirtreeview.cpp +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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 "dirtreeview.h" -#include -#include -#include -#include -#include -#include -#include "dirtreemodel.h" -#include "dirtreemodelitem.h" -#include "filemenu.h" - -namespace Fm { - -DirTreeView::DirTreeView(QWidget* parent): - QTreeView(parent), - currentExpandingItem_(nullptr) { - - setSelectionMode(QAbstractItemView::SingleSelection); - setHeaderHidden(true); - setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - header()->setStretchLastSection(false); - - connect(this, &DirTreeView::collapsed, this, &DirTreeView::onCollapsed); - connect(this, &DirTreeView::expanded, this, &DirTreeView::onExpanded); - - setContextMenuPolicy(Qt::CustomContextMenu); - connect(this, &DirTreeView::customContextMenuRequested, - this, &DirTreeView::onCustomContextMenuRequested); -} - -DirTreeView::~DirTreeView() { -} - -void DirTreeView::cancelPendingChdir() { - if(!pathsToExpand_.empty()) { - pathsToExpand_.clear(); - if(!currentExpandingItem_) { - return; - } - DirTreeModel* _model = static_cast(model()); - disconnect(_model, &DirTreeModel::rowLoaded, this, &DirTreeView::onRowLoaded); - currentExpandingItem_ = nullptr; - } -} - -void DirTreeView::expandPendingPath() { - if(pathsToExpand_.empty()) { - return; - } - - auto path = pathsToExpand_.front(); - // qDebug() << "expanding: " << Path(path).displayBasename(); - DirTreeModel* _model = static_cast(model()); - DirTreeModelItem* item = _model->itemFromPath(path); - // qDebug() << "findItem: " << item; - if(item) { - currentExpandingItem_ = item; - connect(_model, &DirTreeModel::rowLoaded, this, &DirTreeView::onRowLoaded); - if(item->loaded_) { // the node is already loaded - onRowLoaded(item->index()); - } - else { - // _model->loadRow(item->index()); - item->loadFolder(); - } - } - else { - selectionModel()->clear(); - /* since we never get it loaded we need to update cwd here */ - currentPath_ = path; - - cancelPendingChdir(); // FIXME: is this correct? this is not done in the gtk+ version of libfm. - } -} - -void DirTreeView::onRowLoaded(const QModelIndex& index) { - DirTreeModel* _model = static_cast(model()); - if(!currentExpandingItem_) { - return; - } - if(currentExpandingItem_ != _model->itemFromIndex(index)) { - return; - } - /* disconnect the handler since we only need it once */ - disconnect(_model, &DirTreeModel::rowLoaded, this, &DirTreeView::onRowLoaded); - - // DirTreeModelItem* item = _model->itemFromIndex(index); - // qDebug() << "row loaded: " << item->displayName_; - /* after the folder is loaded, the files should have been added to - * the tree model */ - expand(index); - - /* remove the expanded path from pending list */ - pathsToExpand_.erase(pathsToExpand_.begin()); - if(pathsToExpand_.empty()) { /* this is the last one and we're done, select the item */ - // qDebug() << "Done!"; - selectionModel()->select(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Clear); - scrollTo(index, QAbstractItemView::EnsureVisible); - } - else { /* continue expanding next pending path */ - expandPendingPath(); - } -} - - -void DirTreeView::setCurrentPath(Fm::FilePath path) { - DirTreeModel* _model = static_cast(model()); - if(!_model) { - return; - } - int rowCount = _model->rowCount(QModelIndex()); - if(rowCount <= 0 || currentPath_ == path) { - return; - } - - currentPath_ = std::move(path); - - // NOTE: The content of each node is loaded on demand dynamically. - // So, when we ask for a chdir operation, some nodes do not exists yet. - // We have to wait for the loading of child nodes and continue the - // pending chdir operation after the child nodes become available. - - // cancel previous pending tree expansion - cancelPendingChdir(); - - /* find a root item containing this path */ - Fm::FilePath root; - for(int row = 0; row < rowCount; ++row) { - QModelIndex index = _model->index(row, 0, QModelIndex()); - auto row_path = _model->filePath(index); - if(row_path.isPrefixOf(currentPath_)) { - root = row_path; - break; - } - } - - if(root) { /* root item is found */ - path = currentPath_; - do { /* add path elements one by one to a list */ - pathsToExpand_.insert(pathsToExpand_.cbegin(), path); - // qDebug() << "prepend path: " << Path(path).displayBasename(); - if(path == root) { - break; - } - path = path.parent(); - } - while(path); - - expandPendingPath(); - } -} - -void DirTreeView::setModel(QAbstractItemModel* model) { - Q_ASSERT(model->inherits("Fm::DirTreeModel")); - - if(!pathsToExpand_.empty()) { // if a chdir request is in progress, cancel it - cancelPendingChdir(); - } - - QTreeView::setModel(model); - header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); - connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &DirTreeView::onSelectionChanged); -} - -void DirTreeView::mousePressEvent(QMouseEvent* event) { - if(event && event->button() == Qt::RightButton && - event->type() == QEvent::MouseButtonPress) { - // Do not change the selection when the context menu is activated. - return; - } - QTreeView::mousePressEvent(event); -} - -void DirTreeView::onCustomContextMenuRequested(const QPoint& pos) { - QModelIndex index = indexAt(pos); - if(index.isValid()) { - QVariant data = index.data(DirTreeModel::FileInfoRole); - auto fileInfo = data.value>(); - if(fileInfo) { - auto path = fileInfo->path(); - Fm::FileInfoList files ; - files.push_back(fileInfo); - Fm::FileMenu* menu = new Fm::FileMenu(files, fileInfo, path); - // FIXME: apply some settings to the menu and set a proper file launcher to it - Q_EMIT prepareFileMenu(menu); - - QVariant pathData = qVariantFromValue(path); - QAction* action = menu->openAction(); - action->disconnect(); - action->setData(index); - connect(action, &QAction::triggered, this, &DirTreeView::onOpen); - action = new QAction(QIcon::fromTheme("window-new"), tr("Open in New T&ab"), menu); - action->setData(pathData); - connect(action, &QAction::triggered, this, &DirTreeView::onNewTab); - menu->insertAction(menu->separator1(), action); - action = new QAction(QIcon::fromTheme("window-new"), tr("Open in New Win&dow"), menu); - action->setData(pathData); - connect(action, &QAction::triggered, this, &DirTreeView::onNewWindow); - menu->insertAction(menu->separator1(), action); - if(fileInfo->isNative()) { - action = new QAction(QIcon::fromTheme("utilities-terminal"), tr("Open in Termina&l"), menu); - action->setData(pathData); - connect(action, &QAction::triggered, this, &DirTreeView::onOpenInTerminal); - menu->insertAction(menu->separator1(), action); - } - menu->exec(mapToGlobal(pos)); - delete menu; - } - } -} - -void DirTreeView::onOpen() { - if(QAction* action = qobject_cast(sender())) { - setCurrentIndex(action->data().toModelIndex()); - } -} - -void DirTreeView::onNewWindow() { - if(QAction* action = qobject_cast(sender())) { - auto path = action->data().value(); - Q_EMIT openFolderInNewWindowRequested(path); - } -} - -void DirTreeView::onNewTab() { - if(QAction* action = qobject_cast(sender())) { - auto path = action->data().value(); - Q_EMIT openFolderInNewTabRequested(path); - } -} - -void DirTreeView::onOpenInTerminal() { - if(QAction* action = qobject_cast(sender())) { - auto path = action->data().value(); - Q_EMIT openFolderInTerminalRequested(path); - } -} - -void DirTreeView::onNewFolder() { - if(QAction* action = qobject_cast(sender())) { - auto path = action->data().value(); - Q_EMIT createNewFolderRequested(path); - } -} - -void DirTreeView::onCollapsed(const QModelIndex& index) { - DirTreeModel* treeModel = static_cast(model()); - if(treeModel) { - treeModel->unloadRow(index); - } -} - -void DirTreeView::onExpanded(const QModelIndex& index) { - DirTreeModel* treeModel = static_cast(model()); - if(treeModel) { - treeModel->loadRow(index); - } -} -void DirTreeView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) { - // see if to-be-removed items are queued for deletion - // and also clear selection if one of them is selected (otherwise a freeze will occur) - QModelIndex selIndex; - if(selectionModel()->selectedRows().size() == 1) { - selIndex = selectionModel()->selectedRows().at(0); - } - for (int i = start; i <= end; ++i) { - QModelIndex index = parent.child(i, 0); - if(index.isValid()) { - if(index == selIndex) { - selectionModel()->clear(); - } - DirTreeModelItem* item = reinterpret_cast(index.internalPointer()); - if (item->isQueuedForDeletion()) { - queuedForDeletion_.push_back(item); - } - } - } - - QTreeView::rowsAboutToBeRemoved (parent, start, end); -} - -void DirTreeView::rowsRemoved(const QModelIndex& parent, int start, int end) { - QTreeView::rowsRemoved (parent, start, end); - // do the queued deletions only after all rows are removed (otherwise a freeze might occur) - QTimer::singleShot(0, this, SLOT (doQueuedDeletions())); -} - -void DirTreeView::doQueuedDeletions() { - if(!queuedForDeletion_.empty()) { - for(DirTreeModelItem* const item : qAsConst(queuedForDeletion_)) { - delete item; - } - queuedForDeletion_.clear(); - } -} - -void DirTreeView::onSelectionChanged(const QItemSelection& selected, const QItemSelection& /*deselected*/) { - if(!selected.isEmpty()) { - QModelIndex index = selected.first().topLeft(); - DirTreeModel* _model = static_cast(model()); - auto path = _model->filePath(index); - if(path && currentPath_ && path == currentPath_) { - return; - } - cancelPendingChdir(); - if(!path) { - return; - } - currentPath_ = std::move(path); - - // FIXME: use enums for type rather than hard-coded values 0 or 1 - int type = 0; - if(QGuiApplication::mouseButtons() & Qt::MiddleButton) { - type = 1; - } - Q_EMIT chdirRequested(type, currentPath_); - } -} - - -} // namespace Fm diff --git a/src/dirtreeview.h b/src/dirtreeview.h deleted file mode 100644 index b32bd2e..0000000 --- a/src/dirtreeview.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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_DIRTREEVIEW_H -#define FM_DIRTREEVIEW_H - -#include "libfmqtglobals.h" -#include -#include - -#include "core/filepath.h" - -class QItemSelection; - -namespace Fm { - -class FileMenu; -class DirTreeModelItem; - -class LIBFM_QT_API DirTreeView : public QTreeView { - Q_OBJECT - -public: - explicit DirTreeView(QWidget* parent); - ~DirTreeView(); - - const Fm::FilePath& currentPath() const { - return currentPath_; - } - - void setCurrentPath(Fm::FilePath path); - - void chdir(Fm::FilePath path) { - setCurrentPath(std::move(path)); - } - - virtual void setModel(QAbstractItemModel* model); - -protected: - virtual void mousePressEvent(QMouseEvent* event); - virtual void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end); - -private: - void cancelPendingChdir(); - void expandPendingPath(); - -Q_SIGNALS: - void chdirRequested(int type, const Fm::FilePath& path); - void openFolderInNewWindowRequested(const Fm::FilePath& path); - void openFolderInNewTabRequested(const Fm::FilePath& path); - void openFolderInTerminalRequested(const Fm::FilePath& path); - void createNewFolderRequested(const Fm::FilePath& path); - void prepareFileMenu(Fm::FileMenu* menu); // emit before showing a Fm::FileMenu - -protected Q_SLOTS: - void onCollapsed(const QModelIndex& index); - void onExpanded(const QModelIndex& index); - void onRowLoaded(const QModelIndex& index); - void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); - void onCustomContextMenuRequested(const QPoint& pos); - void onOpen(); - void onNewWindow(); - void onNewTab(); - void onOpenInTerminal(); - void onNewFolder(); - void rowsRemoved(const QModelIndex& parent, int start, int end); - void doQueuedDeletions(); - -private: - Fm::FilePath currentPath_; - Fm::FilePathList pathsToExpand_; - DirTreeModelItem* currentExpandingItem_; - std::vector queuedForDeletion_; -}; - -} - -#endif // FM_DIRTREEVIEW_H diff --git a/src/dndactionmenu.cpp b/src/dndactionmenu.cpp deleted file mode 100644 index 2688376..0000000 --- a/src/dndactionmenu.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 "dndactionmenu.h" - -namespace Fm { - -DndActionMenu::DndActionMenu(Qt::DropActions possibleActions, QWidget* parent) - : QMenu(parent) - , copyAction(nullptr) - , moveAction(nullptr) - , linkAction(nullptr) - , cancelAction(nullptr) { - if(possibleActions.testFlag(Qt::CopyAction)) { - copyAction = addAction(QIcon::fromTheme("edit-copy"), tr("Copy here")); - } - if(possibleActions.testFlag(Qt::MoveAction)) { - moveAction = addAction(tr("Move here")); - } - if(possibleActions.testFlag(Qt::LinkAction)) { - linkAction = addAction(tr("Create symlink here")); - } - addSeparator(); - cancelAction = addAction(tr("Cancel")); -} - -DndActionMenu::~DndActionMenu() { - -} - -Qt::DropAction DndActionMenu::askUser(Qt::DropActions possibleActions, QPoint pos) { - Qt::DropAction result = Qt::IgnoreAction; - DndActionMenu menu{possibleActions}; - QAction* action = menu.exec(pos); - if(nullptr != action) { - if(action == menu.copyAction) { - result = Qt::CopyAction; - } - else if(action == menu.moveAction) { - result = Qt::MoveAction; - } - else if(action == menu.linkAction) { - result = Qt::LinkAction; - } - } - return result; -} - - -} // namespace Fm diff --git a/src/dndactionmenu.h b/src/dndactionmenu.h deleted file mode 100644 index d4dd1bd..0000000 --- a/src/dndactionmenu.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_DNDACTIONMENU_H -#define FM_DNDACTIONMENU_H - -#include "libfmqtglobals.h" -#include -#include - -namespace Fm { - -class DndActionMenu : public QMenu { - Q_OBJECT -public: - explicit DndActionMenu(Qt::DropActions possibleActions, QWidget* parent = 0); - virtual ~DndActionMenu(); - - static Qt::DropAction askUser(Qt::DropActions possibleActions, QPoint pos); - -private: - QAction* copyAction; - QAction* moveAction; - QAction* linkAction; - QAction* cancelAction; -}; - -} - -#endif // FM_DNDACTIONMENU_H diff --git a/src/dnddest.cpp b/src/dnddest.cpp deleted file mode 100644 index b0f35c9..0000000 --- a/src/dnddest.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 "dnddest.h" -#include "fileoperation.h" -#include "utilities.h" - -namespace Fm { - -const char* supportedMimeTypes[] = { - "text/uri-list" - "XdndDirectSave0"/* X direct save */ - /* TODO: add more targets to support: text types, _NETSCAPE_URL, property/bgimage ... */ -}; - -DndDest::DndDest() { - -} - -DndDest::~DndDest() { - -} - -bool DndDest::dropMimeData(const QMimeData* data, Qt::DropAction action) { - // FIXME: should we put this in dropEvent handler of FolderView instead? - if(data->hasUrls()) { - qDebug("drop action: %d", action); - auto srcPaths = pathListFromQUrls(data->urls()); - switch(action) { - case Qt::CopyAction: - FileOperation::copyFiles(srcPaths, destPath_); - break; - case Qt::MoveAction: - FileOperation::moveFiles(srcPaths, destPath_); - break; - case Qt::LinkAction: - FileOperation::symlinkFiles(srcPaths, destPath_); - /* Falls through. */ - default: - return false; - } - return true; - } - return false; -} - -bool DndDest::isSupported(const QMimeData* /*data*/) { - return false; -} - -bool DndDest::isSupported(QString /*mimeType*/) { - return false; -} - - -} // namespace Fm diff --git a/src/dnddest.h b/src/dnddest.h deleted file mode 100644 index 3efafd4..0000000 --- a/src/dnddest.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_DNDDEST_H -#define FM_DNDDEST_H - -#include -#include "core/filepath.h" - -namespace Fm { - -class DndDest { -public: - explicit DndDest(); - ~DndDest(); - - void setDestPath(Fm::FilePath dest) { - destPath_ = std::move(dest); - } - - const Fm::FilePath& destPath() { - return destPath_; - } - - bool isSupported(const QMimeData* data); - bool isSupported(QString mimeType); - - bool dropMimeData(const QMimeData* data, Qt::DropAction action); - -private: - Fm::FilePath destPath_; -}; - -} - -#endif // FM_DNDDEST_H diff --git a/src/edit-bookmarks.ui b/src/edit-bookmarks.ui deleted file mode 100644 index 8d989e6..0000000 --- a/src/edit-bookmarks.ui +++ /dev/null @@ -1,143 +0,0 @@ - - - EditBookmarksDialog - - - - 0 - 0 - 480 - 320 - - - - Edit Bookmarks - - - - - - true - - - true - - - QAbstractItemView::InternalMove - - - Qt::MoveAction - - - false - - - false - - - 100 - - - - Name - - - - - Location - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - &Add Item - - - - - - - - - - &Remove Item - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Use drag and drop to reorder the items - - - - - - - - - buttonBox - accepted() - EditBookmarksDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - EditBookmarksDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/editbookmarksdialog.cpp b/src/editbookmarksdialog.cpp deleted file mode 100644 index d9f70e4..0000000 --- a/src/editbookmarksdialog.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 "editbookmarksdialog.h" -#include "ui_edit-bookmarks.h" -#include -#include -#include -#include -#include - -namespace Fm { - -EditBookmarksDialog::EditBookmarksDialog(FmBookmarks* bookmarks, QWidget* parent, Qt::WindowFlags f): - QDialog(parent, f), - ui(new Ui::EditBookmarksDialog()), - bookmarks_(FM_BOOKMARKS(g_object_ref(bookmarks))) { - - ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); // auto delete on close - - // load bookmarks - GList* allBookmarks = fm_bookmarks_get_all(bookmarks_); - for(GList* l = allBookmarks; l; l = l->next) { - FmBookmarkItem* bookmark = reinterpret_cast(l->data); - QTreeWidgetItem* item = new QTreeWidgetItem(); - char* path_str = fm_path_display_name(bookmark->path, false); - item->setData(0, Qt::DisplayRole, QString::fromUtf8(bookmark->name)); - item->setData(1, Qt::DisplayRole, QString::fromUtf8(path_str)); - item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled); - g_free(path_str); - ui->treeWidget->addTopLevelItem(item); - } - g_list_free_full(allBookmarks, (GDestroyNotify)fm_bookmark_item_unref); - - connect(ui->addItem, &QPushButton::clicked, this, &EditBookmarksDialog::onAddItem); - connect(ui->removeItem, &QPushButton::clicked, this, &EditBookmarksDialog::onRemoveItem); -} - -EditBookmarksDialog::~EditBookmarksDialog() { - g_object_unref(bookmarks_); - delete ui; -} - -void EditBookmarksDialog::accept() { - // save bookmarks - // it's easier to recreate the whole bookmark file than - // to manipulate FmBookmarks object. So here we generate the file directly. - // FIXME: maybe in the future we should add a libfm API to easily replace all FmBookmarks. - // Here we use gtk+ 3.0 bookmarks rather than the gtk+ 2.0 one. - // Since gtk+ 2.24.12, gtk+2 reads gtk+3 bookmarks file if it exists. - // So it's safe to only save gtk+3 bookmarks file. - QString path = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); - path += QLatin1String("/gtk-3.0"); - if(!QDir().mkpath(path)) { - return; // fail to create ~/.config/gtk-3.0 dir - } - path += QLatin1String("/bookmarks"); - QSaveFile file(path); // use QSaveFile for atomic file operation - if(file.open(QIODevice::WriteOnly)) { - for(int row = 0; ; ++row) { - QTreeWidgetItem* item = ui->treeWidget->topLevelItem(row); - if(!item) { - break; - } - QString name = item->data(0, Qt::DisplayRole).toString(); - QUrl url = QUrl::fromUserInput(item->data(1, Qt::DisplayRole).toString()); - file.write(url.toEncoded()); - file.write(" "); - file.write(name.toUtf8()); - file.write("\n"); - } - // FIXME: should we support Qt or KDE specific bookmarks in the future? - file.commit(); - } - QDialog::accept(); -} - -void EditBookmarksDialog::onAddItem() { - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setData(0, Qt::DisplayRole, tr("New bookmark")); - item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled); - ui->treeWidget->addTopLevelItem(item); - ui->treeWidget->editItem(item); -} - -void EditBookmarksDialog::onRemoveItem() { - const QList sels = ui->treeWidget->selectedItems(); - for(QTreeWidgetItem* const item : sels) { - delete item; - } -} - - -} // namespace Fm diff --git a/src/editbookmarksdialog.h b/src/editbookmarksdialog.h deleted file mode 100644 index a3f2318..0000000 --- a/src/editbookmarksdialog.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_EDITBOOKMARKSDIALOG_H -#define FM_EDITBOOKMARKSDIALOG_H - -#include "libfmqtglobals.h" -#include -#include - -namespace Ui { -class EditBookmarksDialog; -} - -namespace Fm { - -class LIBFM_QT_API EditBookmarksDialog : public QDialog { - Q_OBJECT -public: - explicit EditBookmarksDialog(FmBookmarks* bookmarks, QWidget* parent = 0, Qt::WindowFlags f = 0); - virtual ~EditBookmarksDialog(); - - virtual void accept(); - -private Q_SLOTS: - void onAddItem(); - void onRemoveItem(); - -private: - Ui::EditBookmarksDialog* ui; - FmBookmarks* bookmarks_; -}; - -} - -#endif // FM_EDITBOOKMARKSDIALOG_H diff --git a/src/exec-file.ui b/src/exec-file.ui deleted file mode 100644 index c5a9ea3..0000000 --- a/src/exec-file.ui +++ /dev/null @@ -1,163 +0,0 @@ - - - ExecFileDialog - - - - 0 - 0 - 487 - 58 - - - - Execute file - - - - - - - - - - - - - - true - - - - - - - - - - - &Open - - - - - - true - - - - - - - E&xecute - - - - - - - - - - Execute in &Terminal - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Cancel - - - - - - - - - - - - - - cancel - clicked() - ExecFileDialog - reject() - - - 341 - 39 - - - 196 - 28 - - - - - exec - clicked() - ExecFileDialog - accept() - - - 56 - 39 - - - 196 - 28 - - - - - execTerm - clicked() - ExecFileDialog - accept() - - - 201 - 39 - - - 196 - 28 - - - - - open - clicked() - ExecFileDialog - accept() - - - 346 - 39 - - - 250 - 28 - - - - - diff --git a/src/execfiledialog.cpp b/src/execfiledialog.cpp deleted file mode 100644 index b0b2780..0000000 --- a/src/execfiledialog.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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 "execfiledialog_p.h" -#include "ui_exec-file.h" -#include "core/iconinfo.h" - -namespace Fm { - -ExecFileDialog::ExecFileDialog(const FileInfo &fileInfo, QWidget* parent, Qt::WindowFlags f): - QDialog(parent, f), - ui(new Ui::ExecFileDialog()), - result_(BasicFileLauncher::ExecAction::DIRECT_EXEC) { - - ui->setupUi(this); - // show file icon - auto gicon = fileInfo.icon(); - if(gicon) { - ui->icon->setPixmap(gicon->qicon().pixmap(QSize(48, 48))); - } - - QString msg; - if(fileInfo.isDesktopEntry()) { - msg = tr("This file '%1' seems to be a desktop entry.\nWhat do you want to do with it?") - .arg(fileInfo.displayName()); - ui->exec->setDefault(true); - ui->execTerm->hide(); - } - else if(fileInfo.isText()) { - msg = tr("This text file '%1' seems to be an executable script.\nWhat do you want to do with it?") - .arg(fileInfo.displayName()); - ui->execTerm->setDefault(true); - } - else { - msg = tr("This file '%1' is executable. Do you want to execute it?") - .arg(fileInfo.displayName()); - ui->exec->setDefault(true); - ui->open->hide(); - } - ui->msg->setText(msg); -} - -ExecFileDialog::~ExecFileDialog() { - delete ui; -} - -void ExecFileDialog::accept() { - QObject* _sender = sender(); - if(_sender == ui->exec) { - result_ = BasicFileLauncher::ExecAction::DIRECT_EXEC; - } - else if(_sender == ui->execTerm) { - result_ = BasicFileLauncher::ExecAction::EXEC_IN_TERMINAL; - } - else if(_sender == ui->open) { - result_ = BasicFileLauncher::ExecAction::OPEN_WITH_DEFAULT_APP; - } - else { - result_ = BasicFileLauncher::ExecAction::CANCEL; - } - QDialog::accept(); -} - -void ExecFileDialog::reject() { - result_ = BasicFileLauncher::ExecAction::CANCEL; - QDialog::reject(); -} - -} // namespace Fm diff --git a/src/execfiledialog_p.h b/src/execfiledialog_p.h deleted file mode 100644 index 8ceae1a..0000000 --- a/src/execfiledialog_p.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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_EXECFILEDIALOG_H -#define FM_EXECFILEDIALOG_H - -#include "core/basicfilelauncher.h" -#include "core/fileinfo.h" - -#include - -#include - -namespace Ui { - class ExecFileDialog; -} - -namespace Fm { - -class ExecFileDialog : public QDialog { - Q_OBJECT -public: - ~ExecFileDialog(); - ExecFileDialog(const FileInfo& fileInfo, QWidget* parent = 0, Qt::WindowFlags f = 0); - - BasicFileLauncher::ExecAction result() { - return result_; - } - -protected: - virtual void accept() override; - virtual void reject() override; - -private: - Ui::ExecFileDialog* ui; - BasicFileLauncher::ExecAction result_; -}; - -} - -#endif // FM_EXECFILEDIALOG_H diff --git a/src/file-operation-dialog.ui b/src/file-operation-dialog.ui deleted file mode 100644 index 47f8c67..0000000 --- a/src/file-operation-dialog.ui +++ /dev/null @@ -1,192 +0,0 @@ - - - FileOperationDialog - - - - 0 - 0 - 450 - 297 - - - - - - - - - - - - - - - - - QFormLayout::AllNonFixedFieldsGrow - - - - - Destination: - - - - - - - - 0 - 0 - - - - - - - true - - - - - - - Processing: - - - - - - - - 0 - 0 - - - - Preparing... - - - - - - - Progress - - - - - - - Qt::AlignCenter - - - - - - - - 0 - 0 - - - - Time remaining: - - - - - - - - 0 - 0 - - - - - - - - - - - - 0 - 0 - - - - - - - - Files processed: - - - - - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel - - - - - - - - Fm::ElidedLabel - QLabel -
fileoperationdialog_p.h
-
-
- - - - buttonBox - accepted() - FileOperationDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - FileOperationDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - -
diff --git a/src/file-props.ui b/src/file-props.ui deleted file mode 100644 index 9454fa0..0000000 --- a/src/file-props.ui +++ /dev/null @@ -1,733 +0,0 @@ - - - FilePropsDialog - - - - 0 - 0 - 424 - 456 - - - - File Properties - - - - - - - - - 10 - - - 10 - - - 10 - - - 10 - - - - - 0 - - - - General - - - - 12 - - - 6 - - - - - - 0 - 0 - - - - - - - - - - - - - 32 - 32 - - - - - - - - - - - Location: - - - - - - - - 0 - 0 - - - - - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - File type: - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - MIME type: - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - File size: - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - On-disk size: - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Last modified: - - - - - - - - 0 - 0 - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Link target: - - - - - - - - 0 - 0 - - - - - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - Open With: - - - - - - - - 0 - 0 - - - - - - - - Last accessed: - - - - - - - - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - - Permissions - - - - 6 - - - - - - 0 - 0 - - - - Ownership - - - - 12 - - - 6 - - - - - - - - - - - - 0 - 0 - - - - Group: - - - - - - - - 0 - 0 - - - - Owner: - - - - - - - - - - - 0 - 0 - - - - Access Control - - - - - - - 0 - 0 - - - - 0 - - - - - - - Owner: - - - - - - - - 0 - 0 - - - - - - - - Group: - - - - - - - - 0 - 0 - - - - - - - - Other: - - - - - - - - 0 - 0 - - - - - - - - Make the file executable - - - true - - - - - - - - - - - 0 - - - 6 - - - - - - 0 - 0 - - - - Owner: - - - - - - - - 0 - 0 - - - - Read - - - - - - - - 0 - 0 - - - - Write - - - - - - - - 0 - 0 - - - - Execute - - - - - - - - 0 - 0 - - - - Group: - - - - - - - - 0 - 0 - - - - Read - - - - - - - - 0 - 0 - - - - Write - - - - - - - - 0 - 0 - - - - Execute - - - - - - - - 0 - 0 - - - - Other: - - - - - - - - 0 - 0 - - - - Read - - - - - - - - 0 - 0 - - - - Write - - - - - - - - 0 - 0 - - - - Execute - - - - - - - - - Sticky - - - - - - - SetUID - - - - - - - SetGID - - - - - - - Qt::Vertical - - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - false - - - Advanced Mode - - - true - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - Fm::AppChooserComboBox - QComboBox -
appchoosercombobox.h
-
-
- - - - buttonBox - accepted() - FilePropsDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - FilePropsDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - -
diff --git a/src/filedialog.cpp b/src/filedialog.cpp deleted file mode 100644 index 0ef0753..0000000 --- a/src/filedialog.cpp +++ /dev/null @@ -1,965 +0,0 @@ -#include "filedialog.h" -#include "cachedfoldermodel.h" -#include "proxyfoldermodel.h" -#include "utilities.h" -#include "core/fileinfojob.h" -#include "ui_filedialog.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Fm { - - -FileDialog::FileDialog(QWidget* parent, FilePath path) : - QDialog(parent), - ui{new Ui::FileDialog()}, - folderModel_{nullptr}, - proxyModel_{nullptr}, - folder_{nullptr}, - options_{0}, - viewMode_{FolderView::DetailedListMode}, - fileMode_{QFileDialog::AnyFile}, - acceptMode_{QFileDialog::AcceptOpen}, - confirmOverwrite_{true}, - modelFilter_{this} { - - ui->setupUi(this); - - // path bar - connect(ui->location, &PathBar::chdir, [this](const FilePath &path) { - setDirectoryPath(path); - }); - - // side pane - ui->sidePane->setMode(Fm::SidePane::ModePlaces); - connect(ui->sidePane, &SidePane::chdirRequested, [this](int /*type*/, const FilePath &path) { - setDirectoryPath(path); - }); - - // folder view - proxyModel_ = new ProxyFolderModel(this); - proxyModel_->sort(FolderModel::ColumnFileName, Qt::AscendingOrder); - proxyModel_->setThumbnailSize(64); - proxyModel_->setShowThumbnails(true); - - proxyModel_->addFilter(&modelFilter_); - - connect(ui->folderView, &FolderView::clicked, this, &FileDialog::onFileClicked); - ui->folderView->setModel(proxyModel_); - ui->folderView->setAutoSelectionDelay(0); - // set the completer - QCompleter* completer = new QCompleter(this); - completer->setModel(proxyModel_); - ui->fileName->setCompleter(completer); - connect(completer, static_cast(&QCompleter::activated), [this](const QString &text) { - ui->folderView->selectionModel()->clearSelection(); - selectFilePath(directoryPath_.child(text.toLocal8Bit().constData())); - }); - // select typed paths if it they exist - connect(ui->fileName, &QLineEdit::textEdited, [this](const QString& /*text*/) { - disconnect(ui->folderView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FileDialog::onSelectionChanged); - ui->folderView->selectionModel()->clearSelection(); - QStringList parsedNames = parseNames(); - for(auto& name: parsedNames) { - selectFilePath(directoryPath_.child(name.toLocal8Bit().constData())); - } - updateAcceptButtonState(); - updateSaveButtonText(false); - connect(ui->folderView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FileDialog::onSelectionChanged); - }); - // update selection mode for the view - updateSelectionMode(); - - // file type - connect(ui->fileTypeCombo, &QComboBox::currentTextChanged, [this](const QString& text) { - selectNameFilter(text); - }); - ui->fileTypeCombo->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); - ui->fileTypeCombo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - ui->fileTypeCombo->setCurrentIndex(0); - - QShortcut* shortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_H), this); - connect(shortcut, &QShortcut::activated, [this]() { - proxyModel_->setShowHidden(!proxyModel_->showHidden()); - }); - - // setup toolbar buttons - auto toolbar = new QToolBar(this); - // back button - backAction_ = toolbar->addAction(QIcon::fromTheme("go-previous"), tr("Go Back")); - backAction_->setShortcut(QKeySequence(tr("Alt+Left", "Go Back"))); - connect(backAction_, &QAction::triggered, [this]() { - history_.backward(); - setDirectoryPath(history_.currentPath(), FilePath(), false); - }); - // forward button - forwardAction_ = toolbar->addAction(QIcon::fromTheme("go-next"), tr("Go Forward")); - forwardAction_->setShortcut(QKeySequence(tr("Alt+Right", "Go Forward"))); - connect(forwardAction_, &QAction::triggered, [this]() { - history_.forward(); - setDirectoryPath(history_.currentPath(), FilePath(), false); - }); - toolbar->addSeparator(); - // reload button - auto reloadAction = toolbar->addAction(QIcon::fromTheme("view-refresh"), tr("Reload")); - reloadAction->setShortcut(QKeySequence(tr("F5", "Reload"))); - connect(reloadAction, &QAction::triggered, [this]() { - if(folder_ && folder_->isLoaded()) { - QObject::disconnect(lambdaConnection_); - auto selFiles = ui->folderView->selectedFiles(); - ui->folderView->selectionModel()->clear(); - // reselect files on reloading - if(!selFiles.empty() - && selFiles.size() <= 50) { // otherwise senseless and CPU-intensive - lambdaConnection_ = QObject::connect(folder_.get(), &Fm::Folder::finishLoading, [this, selFiles]() { - selectFilesOnReload(selFiles); - }); - } - folder_->reload(); - } - }); - // new folder button - auto newFolderAction = toolbar->addAction(QIcon::fromTheme("folder-new"), tr("Create Folder")); - connect(newFolderAction, &QAction::triggered, this, &FileDialog::onNewFolder); - toolbar->addSeparator(); - // view buttons - auto viewModeGroup = new QActionGroup(this); - iconViewAction_ = toolbar->addAction(style()->standardIcon(QStyle::SP_FileDialogContentsView), tr("Icon View")); - iconViewAction_->setCheckable(true); - connect(iconViewAction_, &QAction::toggled, this, &FileDialog::onViewModeToggled); - viewModeGroup->addAction(iconViewAction_); - thumbnailViewAction_ = toolbar->addAction(style()->standardIcon(QStyle::SP_FileDialogInfoView), tr("Thumbnail View")); - thumbnailViewAction_->setCheckable(true); - connect(thumbnailViewAction_, &QAction::toggled, this, &FileDialog::onViewModeToggled); - viewModeGroup->addAction(thumbnailViewAction_); - compactViewAction_ = toolbar->addAction(style()->standardIcon(QStyle::SP_FileDialogListView), tr("Compact View")); - compactViewAction_->setCheckable(true); - connect(compactViewAction_, &QAction::toggled, this, &FileDialog::onViewModeToggled); - viewModeGroup->addAction(compactViewAction_); - detailedViewAction_ = toolbar->addAction(style()->standardIcon(QStyle::SP_FileDialogDetailedView), tr("Detailed List View")); - detailedViewAction_->setCheckable(true); - connect(detailedViewAction_, &QAction::toggled, this, &FileDialog::onViewModeToggled); - viewModeGroup->addAction(detailedViewAction_); - ui->toolbarLayout->addWidget(toolbar); - - setViewMode(viewMode_); - - // set the default splitter position - setSplitterPos(200); - - // browse to the directory - if(path.isValid()) { - setDirectoryPath(path); - } - else { - goHome(); - } - - // focus the text entry on showing the dialog - QTimer::singleShot(0, ui->fileName, SLOT(setFocus())); -} - -FileDialog::~FileDialog() { - freeFolder(); -} - -int FileDialog::splitterPos() const { - return ui->splitter->sizes().at(0); -} - -void FileDialog::setSplitterPos(int pos) { - QList sizes; - sizes.append(qMax(pos, 0)); - sizes.append(320); - ui->splitter->setSizes(sizes); -} - -// This should always be used instead of getting text directly from the entry. -QStringList FileDialog::parseNames() const { - // parse the file names from the text entry - QStringList parsedNames; - auto fileNames = ui->fileName->text(); - if(!fileNames.isEmpty()) { - /* check if there are multiple file names (containing "), - considering the fact that inside quotes were escaped by \ */ - auto firstQuote = fileNames.indexOf(QLatin1Char('\"')); - auto lastQuote = fileNames.lastIndexOf(QLatin1Char('\"')); - if(firstQuote != -1 && lastQuote != -1 - && firstQuote != lastQuote - && (firstQuote == 0 || fileNames.at(firstQuote - 1) != QLatin1Char('\\')) - && fileNames.at(lastQuote - 1) != QLatin1Char('\\')) { - // split the names - QRegExp sep{"\"\\s+\""}; // separated with " " - parsedNames = fileNames.mid(firstQuote + 1, lastQuote - firstQuote - 1).split(sep); - parsedNames.replaceInStrings(QLatin1String("\\\""), QLatin1String("\"")); - } - else { - parsedNames << fileNames.replace(QLatin1String("\\\""), QLatin1String("\"")); - } - } - return parsedNames; -} - -std::shared_ptr FileDialog::firstSelectedDir() const { - std::shared_ptr selectedFolder = nullptr; - auto list = ui->folderView->selectedFiles(); - for(auto it = list.cbegin(); it != list.cend(); ++it) { - auto& item = *it; - if(item->isDir()) { - selectedFolder = item; - break; - } - } - return selectedFolder; -} - -void FileDialog::accept() { - // handle selected filenames - selectedFiles_.clear(); - - // if a folder is selected in file mode, chdir into it (as QFileDialog does) - // by giving priority to the current index and, if it isn't a folder, - // to the first selected folder - if(fileMode_ != QFileDialog::Directory) { - std::shared_ptr selectedFolder = nullptr; - // check if the current index is a folder - QItemSelectionModel* selModel = ui->folderView->selectionModel(); - QModelIndex cur = selModel->currentIndex(); - if(cur.isValid() && selModel->isSelected(cur)) { - auto file = proxyModel_->fileInfoFromIndex(cur); - if(file && file->isDir()) { - selectedFolder = file; - } - } - if(!selectedFolder) { - selectedFolder = firstSelectedDir(); - } - if(selectedFolder) { - setDirectoryPath(selectedFolder->path()); - return; - } - } - - QStringList parsedNames = parseNames(); - if(parsedNames.isEmpty()) { - // when selecting a dir and the name is not provided, just select current dir in the view - if(fileMode_ == QFileDialog::Directory) { - auto localPath = directoryPath_.localPath(); - if(localPath) { - selectedFiles_.append(QUrl::fromLocalFile(localPath.get())); - } - else { - selectedFiles_.append(directory()); - } - } - else { - QMessageBox::critical(this, tr("Error"), tr("Please select a file")); - return; - } - } - else { - if(fileMode_ != QFileDialog::Directory) { - auto firstName = parsedNames.at(0); - auto childPath = directoryPath_.child(firstName.toLocal8Bit().constData()); - auto info = proxyModel_->fileInfoFromPath(childPath); - if(info) { - // if the typed name belongs to a (nonselected) directory, chdir into it - if(info->isDir()) { - setDirectoryPath(childPath); - return; - } - // overwrite prompt (as in QFileDialog::accept) - if(fileMode_ == QFileDialog::AnyFile - && acceptMode_ != QFileDialog::AcceptOpen - && confirmOverwrite_) { - if (QMessageBox::warning(this, windowTitle(), - tr("%1 already exists.\nDo you want to replace it?") - .arg(firstName), - QMessageBox::Yes | QMessageBox::No, QMessageBox::No) - == QMessageBox::No) { - return; - } - } - } - } - - // get full paths for the filenames and convert them to URLs - for(auto& name: parsedNames) { - // add default filename extension as needed - if(!defaultSuffix_.isEmpty() && name.lastIndexOf('.') == -1) { - name += '.'; - name += defaultSuffix_; - } - auto fullPath = directoryPath_.child(name.toLocal8Bit().constData()); - auto localPath = fullPath.localPath(); - /* add the local path if it exists; otherwise, add the uri */ - if(localPath) { - selectedFiles_.append(QUrl::fromLocalFile(localPath.get())); - } - else { - selectedFiles_.append(QUrl::fromEncoded(fullPath.uri().get())); - } - } - } - - // check existence of the selected files and if their types are correct - // async operation, call doAccept() in the callback. - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); - - auto pathList = pathListFromQUrls(selectedFiles_); - auto job = new FileInfoJob(pathList); - job->setAutoDelete(true); - connect(job, &Job::finished, this, &FileDialog::onFileInfoJobFinished); - job->runAsync(); -} - -void FileDialog::reject() { - QDialog::reject(); -} - -void FileDialog::setDirectory(const QUrl &directory) { - auto path = Fm::FilePath::fromUri(directory.toEncoded().constData()); - setDirectoryPath(path); -} - -// interface for QPlatformFileDialogHelper - -void FileDialog::freeFolder() { - if(folder_) { - QObject::disconnect(lambdaConnection_); // lambdaConnection_ can be invalid - disconnect(folder_.get(), nullptr, this, nullptr); - folder_ = nullptr; - } -} - -void FileDialog::goHome() { - setDirectoryPath(FilePath::homeDir()); -} - -void FileDialog::setDirectoryPath(FilePath directory, FilePath selectedPath, bool addHistory) { - if(!directory.isValid()) { - updateAcceptButtonState(); // FIXME: is this needed? - return; - } - - if(directoryPath_ != directory) { - if(folder_) { - if(folderModel_) { - proxyModel_->setSourceModel(nullptr); - folderModel_->unref(); // unref the cached model - folderModel_ = nullptr; - } - freeFolder(); - } - - directoryPath_ = std::move(directory); - - ui->location->setPath(directoryPath_); - ui->sidePane->chdir(directoryPath_); - if(addHistory) { - history_.add(directoryPath_); - } - backAction_->setEnabled(history_.canBackward()); - forwardAction_->setEnabled(history_.canForward()); - - folder_ = Fm::Folder::fromPath(directoryPath_); - folderModel_ = CachedFolderModel::modelFromFolder(folder_); - proxyModel_->setSourceModel(folderModel_); - - // no lambda in these connections for easy disconnection - connect(folder_.get(), &Fm::Folder::removed, this, &FileDialog::goHome); - connect(folder_.get(), &Fm::Folder::unmount, this, &FileDialog::goHome); - - QUrl uri = QUrl::fromEncoded(directory.uri().get()); - Q_EMIT directoryEntered(uri); - } - - // select the path if valid - if(selectedPath.isValid()) { - if(folder_->isLoaded()) { - selectFilePathWithDelay(selectedPath); - } - else { - lambdaConnection_ = QObject::connect(folder_.get(), &Fm::Folder::finishLoading, [this, selectedPath]() { - selectFilePathWithDelay(selectedPath); - }); - } - } - else { - updateAcceptButtonState(); - updateSaveButtonText(false); - } - -} - -void FileDialog::selectFilePath(const FilePath &path) { - auto idx = proxyModel_->indexFromPath(path); - if(!idx.isValid()) { - return; - } - - // FIXME: add a method to Fm::FolderView to select files - - // FIXME: need to add this for detailed list - QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Select; - if(viewMode_ == FolderView::DetailedListMode) { - flags |= QItemSelectionModel::Rows; - } - QItemSelectionModel* selModel = ui->folderView->selectionModel(); - selModel->select(idx, flags); - selModel->setCurrentIndex(idx, QItemSelectionModel::Current); - QTimer::singleShot(0, this, [this, idx]() { - ui->folderView->childView()->scrollTo(idx, QAbstractItemView::PositionAtCenter); - }); -} - -void FileDialog::selectFilePathWithDelay(const FilePath &path) { - QTimer::singleShot(0, this, [this, path]() { - if(acceptMode_ == QFileDialog::AcceptSave) { - // with a save dialog, always put the base name in line-edit, regardless of selection - ui->fileName->setText(path.baseName().get()); - } - // update "accept" button because there might be no selection later - updateAcceptButtonState(); - updateSaveButtonText(false); - // try to select path - selectFilePath(path); - }); -} - -void FileDialog::selectFilesOnReload(const Fm::FileInfoList& infos) { - QObject::disconnect(lambdaConnection_); - QTimer::singleShot(0, this, [this, infos]() { - for(auto& fileInfo: infos) { - selectFilePath(fileInfo->path()); - } - }); -} - -void FileDialog::onCurrentRowChanged(const QModelIndex ¤t, const QModelIndex& /*previous*/) { - // emit currentChanged signal - QUrl currentUrl; - if(current.isValid()) { - // emit changed siangl for newly selected items - auto fi = proxyModel_->fileInfoFromIndex(current); - if(fi) { - currentUrl = QUrl::fromEncoded(fi->path().uri().get()); - } - } - Q_EMIT currentChanged(currentUrl); -} - -void FileDialog::onSelectionChanged(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/) { - auto selFiles = ui->folderView->selectedFiles(); - if(selFiles.empty()) { - updateAcceptButtonState(); - updateSaveButtonText(false); - return; - } - bool multiple(selFiles.size() > 1); - bool hasDir(false); - QString fileNames; - for(auto& fileInfo: selFiles) { - if(fileMode_ == QFileDialog::Directory) { - // if we want to select dir, ignore selected files - if(!fileInfo->isDir()) { - continue; - } - } - else if(fileInfo->isDir()) { - // if we want to select files, ignore selected dirs - hasDir = true; - continue; - } - - auto baseName = fileInfo->path().baseName(); - if(multiple) { - // support multiple selection - if(!fileNames.isEmpty()) { - fileNames += ' '; - } - fileNames += QLatin1Char('\"'); - // escape inside quotes with \ to distinguish between them - // and the quotes used for separating file names from each other - QString name(baseName.get()); - fileNames += name.replace(QLatin1String("\""), QLatin1String("\\\"")); - fileNames += QLatin1Char('\"'); - } - else { - // support single selection only - QString name(baseName.get()); - fileNames = name.replace(QLatin1String("\""), QLatin1String("\\\"")); - break; - } - } - // put the selection list in the text entry - if(!fileNames.isEmpty()) { - ui->fileName->setText(fileNames); - } - updateSaveButtonText(hasDir); - updateAcceptButtonState(); -} - -void FileDialog::onFileClicked(int type, const std::shared_ptr &file) { - bool canAccept = false; - if(file && type == FolderView::ActivatedClick) { - if(file->isDir()) { - if(fileMode_ == QFileDialog::Directory) { - ui->fileName->clear(); - } - // chdir into the activated dir - setDirectoryPath(file->path()); - } - else if(fileMode_ != QFileDialog::Directory) { - // select file(s) and a file item is activated - canAccept = true; - } - } - - if(canAccept) { - selectFilePath(file->path()); - accept(); - } -} - -void FileDialog::onNewFolder() { - createFileOrFolder(CreateNewFolder, directoryPath_, nullptr, this); -} - -void FileDialog::onViewModeToggled(bool active) { - if(active) { - auto action = static_cast(sender()); - FolderView::ViewMode newMode; - if(action == iconViewAction_) { - newMode = FolderView::IconMode; - } - else if(action == thumbnailViewAction_) { - newMode = FolderView::ThumbnailMode; - } - else if(action == compactViewAction_) { - newMode = FolderView::CompactMode; - } - else if(action == detailedViewAction_) { - newMode = FolderView::DetailedListMode; - } - else { - return; - } - setViewMode(newMode); - } -} - -void FileDialog::updateSelectionMode() { - // enable multiple selection? - ui->folderView->childView()->setSelectionMode(fileMode_ == QFileDialog::ExistingFiles ? QAbstractItemView::ExtendedSelection : QAbstractItemView::SingleSelection); -} - -void FileDialog::doAccept() { - - Q_EMIT filesSelected(selectedFiles_); - - if(selectedFiles_.size() == 1) { - Q_EMIT fileSelected(selectedFiles_[0]); - } - - QDialog::accept(); -} - -void FileDialog::onFileInfoJobFinished() { - auto job = static_cast(sender()); - if(job->isCancelled()) { - selectedFiles_.clear(); - reject(); - } - else { - QString error; - // check if the files exist and their types are correct - auto paths = job->paths(); - auto files = job->files(); - for(size_t i = 0; i < paths.size(); ++i) { - const auto& path = paths[i]; - if(i >= files.size() || files[i]->path() != path) { - // the file path is not found and does not have file info - if(fileMode_ != QFileDialog::AnyFile) { - // if we do not allow non-existent file, this is an error. - error = tr("Path \"%1\" does not exist").arg(path.displayName().get()); - break; - } - ++i; // skip the file - continue; - } - - // FIXME: currently, if a path is not found, FmFileInfoJob does not return its file info object. - // This is bad API design. We may return nullptr for the failed file info query instead. - const auto& file = files[i]; - // check if the file type is correct - if(fileMode_ == QFileDialog::Directory) { - if(!file->isDir()) { - // we're selecting dirs, but the selected file path does not point to a dir - error = tr("\"%1\" is not a directory").arg(path.displayName().get()); - break; - } - } - else if(file->isDir() || file->isShortcut()) { - // we're selecting files, but the selected file path refers to a dir or shortcut (such as computer:///) - error = tr("\"%1\" is not a file").arg(path.displayName().get());; - break; - } - } - - if(error.isEmpty()) { - // no error! - doAccept(); - } - else { - QMessageBox::critical(this, tr("Error"), error); - selectedFiles_.clear(); - } - } - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true); -} - -QUrl FileDialog::directory() const { - QUrl url{directoryPath_.uri().get()}; - return url; -} - -void FileDialog::selectFile(const QUrl& filename) { - auto urlStr = filename.toEncoded(); - auto path = FilePath::fromUri(urlStr.constData()); - auto parent = path.parent(); - // chdir into file's parent if needed and select the file - setDirectoryPath(parent, path); -} - -QList FileDialog::selectedFiles() { - return selectedFiles_; -} - -void FileDialog::selectNameFilter(const QString& filter) { - if(filter != currentNameFilter_) { - currentNameFilter_ = filter; - ui->fileTypeCombo->setCurrentText(filter); - - modelFilter_.update(); - proxyModel_->invalidate(); - Q_EMIT filterSelected(filter); - } -} - -void FileDialog::selectMimeTypeFilter(const QString &filter) { - auto idx = mimeTypeFilters_.indexOf(filter); - if(idx != -1) { - ui->fileTypeCombo->setCurrentIndex(idx); - } -} - -QString FileDialog::selectedMimeTypeFilter() const { - QString filter; - auto idx = mimeTypeFilters_.indexOf(filter); - if(idx >= 0 && idx < mimeTypeFilters_.size()) { - filter = mimeTypeFilters_[idx]; - } - return filter; -} - -bool FileDialog::isSupportedUrl(const QUrl& url) { - auto scheme = url.scheme().toLocal8Bit(); - // FIXME: this is not reliable due to the bug of gvfs. - return Fm::isUriSchemeSupported(scheme.constData()); -} - - -// options - -void FileDialog::setFilter(QDir::Filters filters) { - filters_ = filters; - // TODO: -} - -void FileDialog::setViewMode(FolderView::ViewMode mode) { - viewMode_ = mode; - - // Since setModel() is called by FolderView::setViewMode(), the selectionModel will be replaced by one - // created by the view. So, we need to deal with selection changes again after setting the view mode. - disconnect(ui->folderView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &FileDialog::onCurrentRowChanged); - disconnect(ui->folderView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FileDialog::onSelectionChanged); - - ui->folderView->setViewMode(mode); - switch(mode) { - case FolderView::IconMode: - iconViewAction_->setChecked(true); - break; - case FolderView::ThumbnailMode: - thumbnailViewAction_->setChecked(true); - break; - case FolderView::CompactMode: - compactViewAction_->setChecked(true); - break; - case FolderView::DetailedListMode: - detailedViewAction_->setChecked(true); - break; - default: - break; - } - // selection changes - connect(ui->folderView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &FileDialog::onCurrentRowChanged); - connect(ui->folderView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FileDialog::onSelectionChanged); - // update selection mode for the view - updateSelectionMode(); -} - - -void FileDialog::setFileMode(QFileDialog::FileMode mode) { - if(mode == QFileDialog::DirectoryOnly) { - // directly only is deprecated and not allowed. - mode = QFileDialog::Directory; - } - fileMode_ = mode; - - // enable multiple selection? - updateSelectionMode(); -} - - -void FileDialog::setAcceptMode(QFileDialog::AcceptMode mode) { - acceptMode_ = mode; - // set "open/save" label if it isn't set explicitly - if(isLabelExplicitlySet(QFileDialog::Accept)) { - return; - } - if(acceptMode_ == QFileDialog::AcceptOpen) { - setLabelTextControl(QFileDialog::Accept, tr("&Open")); - } - else if(acceptMode_ == QFileDialog::AcceptSave) { - setLabelTextControl(QFileDialog::Accept, tr("&Save")); - } -} - -void FileDialog::setNameFilters(const QStringList& filters) { - if(filters.isEmpty()) { - // default filename pattern - nameFilters_ = (QStringList() << tr("All Files (*)")); - } - else { - nameFilters_ = filters; - } - ui->fileTypeCombo->clear(); - ui->fileTypeCombo->addItems(nameFilters_); -} - -void FileDialog::setMimeTypeFilters(const QStringList& filters) { - mimeTypeFilters_ = filters; - - QStringList nameFilters; - QMimeDatabase db; - for(const auto& filter: filters) { - auto mimeType = db.mimeTypeForName(filter); - auto nameFilter = mimeType.comment(); - if(!mimeType.suffixes().empty()) { - nameFilter + " ("; - const auto suffixes = mimeType.suffixes(); - for(const auto& suffix: suffixes) { - nameFilter += "*."; - nameFilter += suffix; - nameFilter += ' '; - } - nameFilter[nameFilter.length() - 1] = ')'; - } - nameFilters << nameFilter; - } - setNameFilters(nameFilters); -} - -void FileDialog::setLabelTextControl(QFileDialog::DialogLabel label, const QString& text) { - switch(label) { - case QFileDialog::LookIn: - ui->lookInLabel->setText(text); - break; - case QFileDialog::FileName: - ui->fileNameLabel->setText(text); - break; - case QFileDialog::FileType: - ui->fileTypeLabel->setText(text); - break; - case QFileDialog::Accept: - ui->buttonBox->button(QDialogButtonBox::Ok)->setText(text); - break; - case QFileDialog::Reject: - ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(text); - break; - default: - break; - } -} - -void FileDialog::setLabelText(QFileDialog::DialogLabel label, const QString& text) { - setLabelExplicitly(label, text); - setLabelTextControl(label, text); -} - -QString FileDialog::labelText(QFileDialog::DialogLabel label) const { - QString text; - switch(label) { - case QFileDialog::LookIn: - text = ui->lookInLabel->text(); - break; - case QFileDialog::FileName: - text = ui->fileNameLabel->text(); - break; - case QFileDialog::FileType: - text = ui->fileTypeLabel->text(); - break; - case QFileDialog::Accept: - ui->buttonBox->button(QDialogButtonBox::Ok)->text(); - break; - case QFileDialog::Reject: - ui->buttonBox->button(QDialogButtonBox::Cancel)->text(); - break; - default: - break; - } - return text; -} - -void FileDialog::updateSaveButtonText(bool saveOnFolder) { - if(fileMode_ != QFileDialog::Directory - && acceptMode_ == QFileDialog::AcceptSave) { - // change save button to open button when there is a dir with the save name, - // otherwise restore it to a save button again - if(!saveOnFolder) { - QStringList parsedNames = parseNames(); - if(!parsedNames.isEmpty()) { - auto info = proxyModel_->fileInfoFromPath(directoryPath_.child(parsedNames.at(0).toLocal8Bit().constData())); - if(info && info->isDir()) { - saveOnFolder = true; - } - } - } - if(saveOnFolder) { - setLabelTextControl(QFileDialog::Accept, tr("&Open")); - } - else { - // restore save button text appropriately - if(isLabelExplicitlySet(QFileDialog::Accept)) { - setLabelTextControl(QFileDialog::Accept, explicitLabels_[QFileDialog::Accept]); - } - else { - setLabelTextControl(QFileDialog::Accept, tr("&Save")); - } - } - } -} - -void FileDialog::updateAcceptButtonState() { - bool enable(false); - if(fileMode_ != QFileDialog::Directory) { - if(acceptMode_ == QFileDialog::AcceptOpen) - { - if(firstSelectedDir()) { - // enable "open" button if a dir is selected - enable = true; - } - else { - // enable "open" button when there is a file whose name is listed - QStringList parsedNames = parseNames(); - for(auto& name: parsedNames) { - if(proxyModel_->indexFromPath(directoryPath_.child(name.toLocal8Bit().constData())).isValid()) { - enable = true; - break; - } - } - } - } - else if(acceptMode_ == QFileDialog::AcceptSave) { - // enable "save" button when there is a name or a dir selection - if(!ui->fileName->text().isEmpty()) { - enable = true; - } - else if(firstSelectedDir()) { - enable = true; - } - } - } - else if(fileMode_ == QFileDialog::Directory - && acceptMode_ != QFileDialog::AcceptSave) { - QStringList parsedNames = parseNames(); - if(parsedNames.isEmpty()) { - // in the dir mode, the current dir will be opened - // if no dir is selected and the name list is empty - enable = true; - } - else { - for(auto& name: parsedNames) { - auto info = proxyModel_->fileInfoFromPath(directoryPath_.child(name.toLocal8Bit().constData())); - if(info && info->isDir()) { - // the name of a dir is listed - enable = true; - break; - } - } - } - } - else { - enable = true; - } - ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enable); -} - -bool FileDialog::FileDialogFilter::filterAcceptsRow(const ProxyFolderModel* /*model*/, const std::shared_ptr &info) const { - if(dlg_->fileMode_ == QFileDialog::Directory) { - // we only want to select directories - if(!info->isDir()) { // not a dir - // NOTE: here we ignore dlg_->options_& QFileDialog::ShowDirsOnly option. - return false; - } - } - else { - // we want to select files, so all directories can be shown regardless of their names - if(info->isDir()) { - return true; - } - } - - bool nameMatched = false; - auto& name = info->displayName(); - for(const auto& pattern: patterns_) { - if(pattern.exactMatch(name)) { - nameMatched = true; - break; - } - } - return nameMatched; -} - -void FileDialog::FileDialogFilter::update() { - // update filename patterns - patterns_.clear(); - QString nameFilter = dlg_->currentNameFilter_; - // if the filter contains (...), only get the part between the parenthesis. - auto left = nameFilter.indexOf('('); - if(left != -1) { - ++left; - auto right = nameFilter.indexOf(')', left); - if(right == -1) { - right = nameFilter.length(); - } - nameFilter = nameFilter.mid(left, right - left); - } - // parse the "*.ext1 *.ext2 *.ext3 ..." list into QRegExp objects - auto globs = nameFilter.simplified().split(' '); - for(const auto& glob: globs) { - patterns_.emplace_back(QRegExp(glob, Qt::CaseInsensitive, QRegExp::Wildcard)); - } -} - -} // namespace Fm diff --git a/src/filedialog.h b/src/filedialog.h deleted file mode 100644 index 207d00d..0000000 --- a/src/filedialog.h +++ /dev/null @@ -1,213 +0,0 @@ -#ifndef FM_FILEDIALOG_H -#define FM_FILEDIALOG_H - -#include "libfmqtglobals.h" -#include "core/filepath.h" - -#include -#include -#include -#include -#include "folderview.h" -#include "browsehistory.h" - -namespace Ui { -class FileDialog; -} - -namespace Fm { - -class CachedFolderModel; -class ProxyFolderModel; - -class LIBFM_QT_API FileDialog : public QDialog { - Q_OBJECT -public: - explicit FileDialog(QWidget *parent = 0, FilePath path = FilePath::homeDir()); - - ~FileDialog(); - - // Some QFileDialog compatible interface - void accept() override; - - void reject() override; - - QFileDialog::Options options() const { - return options_; - } - - void setOptions(QFileDialog::Options options) { - options_ = options; - } - - // interface for QPlatformFileDialogHelper - - void setDirectory(const QUrl &directory); - - QUrl directory() const; - - void selectFile(const QUrl &filename); - - QList selectedFiles(); - - void selectNameFilter(const QString &filter); - - QString selectedNameFilter() const { - return currentNameFilter_; - } - - void selectMimeTypeFilter(const QString &filter); - - QString selectedMimeTypeFilter() const; - - bool isSupportedUrl(const QUrl &url); - - // options - - // not yet supported - QDir::Filters filter() const { - return filters_; - } - // not yet supported - void setFilter(QDir::Filters filters); - - void setViewMode(FolderView::ViewMode mode); - FolderView::ViewMode viewMode() const { - return viewMode_; - } - - void setFileMode(QFileDialog::FileMode mode); - QFileDialog::FileMode fileMode() const { - return fileMode_; - } - - void setAcceptMode(QFileDialog::AcceptMode mode); - QFileDialog::AcceptMode acceptMode() const { - return acceptMode_; - } - - void setNameFilters(const QStringList &filters); - QStringList nameFilters() const { - return nameFilters_; - } - - void setMimeTypeFilters(const QStringList &filters); - QStringList mimeTypeFilters() const { - return mimeTypeFilters_; - } - - void setDefaultSuffix(const QString &suffix) { - if(!suffix.isEmpty() && suffix[0] == '.') { - // if the first char is dot, remove it. - defaultSuffix_ = suffix.mid(1); - } - else { - defaultSuffix_ = suffix; - } - } - QString defaultSuffix() const { - return defaultSuffix_; - } - - void setConfirmOverwrite(bool enabled) { - confirmOverwrite_ = enabled; - } - bool confirmOverwrite() const { - return confirmOverwrite_; - } - - void setLabelText(QFileDialog::DialogLabel label, const QString &text); - QString labelText(QFileDialog::DialogLabel label) const; - - int splitterPos() const; - void setSplitterPos(int pos); - -private Q_SLOTS: - void onCurrentRowChanged(const QModelIndex ¤t, const QModelIndex& /*previous*/); - void onSelectionChanged(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/); - void onFileClicked(int type, const std::shared_ptr& file); - void onNewFolder(); - void onViewModeToggled(bool active); - void goHome(); - -Q_SIGNALS: - // emitted when the dialog is accepted and some files are selected - void fileSelected(const QUrl &file); - void filesSelected(const QList &files); - - // emitted whenever selection changes (including no selected files) - void currentChanged(const QUrl &path); - - void directoryEntered(const QUrl &directory); - void filterSelected(const QString &filter); - -private: - - class FileDialogFilter: public ProxyFolderModelFilter { - public: - FileDialogFilter(FileDialog* dlg): dlg_{dlg} {} - virtual bool filterAcceptsRow(const ProxyFolderModel* /*model*/, const std::shared_ptr& info) const override; - void update(); - - FileDialog* dlg_; - std::vector patterns_; - }; - - bool isLabelExplicitlySet(QFileDialog::DialogLabel label) const { - return !explicitLabels_[label].isEmpty(); - } - void setLabelExplicitly(QFileDialog::DialogLabel label, const QString& text) { - explicitLabels_[label] = text; - } - void setLabelTextControl(QFileDialog::DialogLabel label, const QString &text); - void updateSaveButtonText(bool saveOnFolder); - void updateAcceptButtonState(); - - std::shared_ptr firstSelectedDir() const; - void selectFilePath(const FilePath& path); - void selectFilePathWithDelay(const FilePath& path); - void selectFilesOnReload(const Fm::FileInfoList& infos); - void setDirectoryPath(FilePath directory, FilePath selectedPath = FilePath(), bool addHistory = true); - void updateSelectionMode(); - void doAccept(); - void onFileInfoJobFinished(); - void freeFolder(); - QStringList parseNames() const; - -private: - std::unique_ptr ui; - CachedFolderModel* folderModel_; - ProxyFolderModel* proxyModel_; - FilePath directoryPath_; - std::shared_ptr folder_; - Fm::BrowseHistory history_; - - QFileDialog::Options options_; - QDir::Filters filters_; - FolderView::ViewMode viewMode_; - QFileDialog::FileMode fileMode_; - QFileDialog::AcceptMode acceptMode_; - bool confirmOverwrite_; - QStringList nameFilters_; - QStringList mimeTypeFilters_; - QString defaultSuffix_; - FileDialogFilter modelFilter_; - QString currentNameFilter_; - QList selectedFiles_; - // view modes: - QAction* iconViewAction_; - QAction* thumbnailViewAction_; - QAction* compactViewAction_; - QAction* detailedViewAction_; - // back and forward buttons: - QAction* backAction_; - QAction* forwardAction_; - // dialog labels that can be set explicitly: - QString explicitLabels_[5]; - // needed for disconnecting Fm::Folder signal from lambda: - QMetaObject::Connection lambdaConnection_; -}; - - -} // namespace Fm -#endif // FM_FILEDIALOG_H diff --git a/src/filedialog.ui b/src/filedialog.ui deleted file mode 100644 index cc7d65e..0000000 --- a/src/filedialog.ui +++ /dev/null @@ -1,151 +0,0 @@ - - - FileDialog - - - - 0 - 0 - 700 - 500 - - - - - - - - - - - - Location: - - - - - - - - - - - - Qt::Horizontal - - - - - 0 - 0 - - - - - 120 - 0 - - - - - - - 1 - 0 - - - - - - - - - - - File name: - - - - - - - - - - File type: - - - - - - - - - - Qt::Vertical - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - - Fm::SidePane - QWidget -
sidepane.h
- 1 -
- - Fm::FolderView - QWidget -
folderview.h
- 1 -
- - Fm::PathBar - QWidget -
pathbar.h
- 1 -
-
- - - - buttonBox - accepted() - FileDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - FileDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - -
diff --git a/src/filelauncher.cpp b/src/filelauncher.cpp deleted file mode 100644 index 5f667fc..0000000 --- a/src/filelauncher.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 "filelauncher.h" -#include "applaunchcontext.h" -#include -#include -#include "execfiledialog_p.h" -#include "appchooserdialog.h" -#include "utilities.h" - -#include "core/fileinfojob.h" -#include "mountoperation.h" - -namespace Fm { - -FileLauncher::FileLauncher() { -} - -FileLauncher::~FileLauncher() { -} - - -bool FileLauncher::launchFiles(QWidget* parent, const FileInfoList &file_infos) { - GObjectPtr context{fm_app_launch_context_new_for_widget(parent), false}; - bool ret = BasicFileLauncher::launchFiles(file_infos, G_APP_LAUNCH_CONTEXT(context.get())); - return ret; -} - -bool FileLauncher::launchPaths(QWidget* parent, const FilePathList& paths) { - GObjectPtr context{fm_app_launch_context_new_for_widget(parent), false}; - bool ret = BasicFileLauncher::launchPaths(paths, G_APP_LAUNCH_CONTEXT(context.get())); - return ret; -} - -int FileLauncher::ask(const char* /*msg*/, char* const* /*btn_labels*/, int /*default_btn*/) { - /* FIXME: set default button properly */ - // return fm_askv(data->parent, nullptr, msg, btn_labels); - return -1; -} - -GAppInfoPtr FileLauncher::chooseApp(const FileInfoList& /*fileInfos*/, const char *mimeType, GErrorPtr& /*err*/) { - AppChooserDialog dlg(nullptr); - GAppInfoPtr app; - if(mimeType) { - dlg.setMimeType(Fm::MimeType::fromName(mimeType)); - } - else { - dlg.setCanSetDefault(false); - } - // FIXME: show error properly? - if(execModelessDialog(&dlg) == QDialog::Accepted) { - app = dlg.selectedApp(); - } - return app; -} - -bool FileLauncher::openFolder(GAppLaunchContext *ctx, const FileInfoList &folderInfos, GErrorPtr &err) { - return BasicFileLauncher::openFolder(ctx, folderInfos, err); -} - -bool FileLauncher::showError(GAppLaunchContext* /*ctx*/, GErrorPtr &err, const FilePath &path, const FileInfoPtr &info) { - /* ask for mount if trying to launch unmounted path */ - if(err->domain == G_IO_ERROR) { - if(path && err->code == G_IO_ERROR_NOT_MOUNTED) { - MountOperation* op = new MountOperation(true); - op->setAutoDestroy(true); - if(info && info->isMountable()) { - // this is a mountable shortcut (such as computer:///xxxx.drive) - op->mountMountable(path); - } - else { - op->mountEnclosingVolume(path); - } - if(op->wait()) { - // if the mount operation succeeds, we can ignore the error and continue - return true; - } - } - else if(err->code == G_IO_ERROR_FAILED_HANDLED) { - return true; /* don't show error message */ - } - } - QMessageBox dlg(QMessageBox::Critical, QObject::tr("Error"), QString::fromUtf8(err->message), QMessageBox::Ok); - execModelessDialog(&dlg); - return false; -} - -BasicFileLauncher::ExecAction FileLauncher::askExecFile(const FileInfoPtr &file) { - auto res = BasicFileLauncher::ExecAction::CANCEL; - ExecFileDialog dlg(*file); - if(execModelessDialog(&dlg) == QDialog::Accepted) { - res = dlg.result(); - } - return res; -} - - -} // namespace Fm diff --git a/src/filelauncher.h b/src/filelauncher.h deleted file mode 100644 index be5be5a..0000000 --- a/src/filelauncher.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_FILELAUNCHER_H -#define FM_FILELAUNCHER_H - -#include "libfmqtglobals.h" -#include -#include "core/fileinfo.h" -#include "core/basicfilelauncher.h" - -namespace Fm { - -class LIBFM_QT_API FileLauncher: public BasicFileLauncher { -public: - explicit FileLauncher(); - virtual ~FileLauncher(); - - bool launchFiles(QWidget* parent, const FileInfoList& file_infos); - - bool launchPaths(QWidget* parent, const FilePathList &paths); - -protected: - - GAppInfoPtr chooseApp(const FileInfoList& fileInfos, const char* mimeType, GErrorPtr& err) override; - - bool openFolder(GAppLaunchContext* ctx, const FileInfoList& folderInfos, GErrorPtr& err) override; - - bool showError(GAppLaunchContext* ctx, GErrorPtr& err, const FilePath& path = FilePath{}, const FileInfoPtr& info = FileInfoPtr{}) override; - - ExecAction askExecFile(const FileInfoPtr& file) override; - - int ask(const char* msg, char* const* btn_labels, int default_btn) override; -}; - -} - -#endif // FM_FILELAUNCHER_H diff --git a/src/filemenu.cpp b/src/filemenu.cpp deleted file mode 100644 index 97b0757..0000000 --- a/src/filemenu.cpp +++ /dev/null @@ -1,414 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 "filemenu.h" -#include "createnewmenu.h" -#include "filepropsdialog.h" -#include "utilities.h" -#include "fileoperation.h" -#include "filelauncher.h" -#include "appchooserdialog.h" - -#include "customactions/fileaction.h" -#include "customaction_p.h" - -#include -#include -#include -#include "filemenu_p.h" - -#include "core/archiver.h" - -namespace Fm { - -FileMenu::FileMenu(Fm::FileInfoList files, std::shared_ptr info, Fm::FilePath cwd, bool isWritableDir, const QString& title, QWidget* parent): - QMenu(title, parent), - files_{std::move(files)}, - info_{std::move(info)}, - cwd_{std::move(cwd)}, - unTrashAction_(nullptr), - fileLauncher_(nullptr) { - - useTrash_ = true; - confirmDelete_ = true; - confirmTrash_ = false; // Confirm before moving files into "trash can" - - openAction_ = nullptr; - openWithMenuAction_ = nullptr; - openWithAction_ = nullptr; - separator1_ = nullptr; - cutAction_ = nullptr; - copyAction_ = nullptr; - pasteAction_ = nullptr; - deleteAction_ = nullptr; - unTrashAction_ = nullptr; - renameAction_ = nullptr; - separator2_ = nullptr; - propertiesAction_ = nullptr; - - auto mime_type = info_->mimeType(); - Fm::FilePath path = info_->path(); - - // check if the files are of the same type - sameType_ = files_.isSameType(); - // check if the files are on the same filesystem - sameFilesystem_ = files_.isSameFilesystem(); - // check if the files are all virtual - - // FIXME: allVirtual_ = sameFilesystem_ && fm_path_is_virtual(path); - allVirtual_ = false; - - // check if the files are all in the trash can - allTrash_ = sameFilesystem_ && path.hasUriScheme("trash"); - - openAction_ = new QAction(QIcon::fromTheme("document-open"), tr("Open"), this); - connect(openAction_, &QAction::triggered, this, &FileMenu::onOpenTriggered); - addAction(openAction_); - - openWithMenuAction_ = new QAction(tr("Open With..."), this); - addAction(openWithMenuAction_); - // create the "Open with..." sub menu - QMenu* menu = new QMenu(this); - openWithMenuAction_->setMenu(menu); - - if(sameType_) { /* add specific menu items for this mime type */ - if(mime_type && !allVirtual_) { /* the file has a valid mime-type and its not virtual */ - GList* apps = g_app_info_get_all_for_type(mime_type->name()); - GList* l; - for(l = apps; l; l = l->next) { - Fm::GAppInfoPtr app{G_APP_INFO(l->data), false}; - // check if the command really exists - gchar* program_path = g_find_program_in_path(g_app_info_get_executable(app.get())); - if(!program_path) { - continue; - } - g_free(program_path); - - // create a QAction for the application. - AppInfoAction* action = new AppInfoAction(std::move(app), menu); - connect(action, &QAction::triggered, this, &FileMenu::onApplicationTriggered); - menu->addAction(action); - } - g_list_free(apps); - } - } - menu->addSeparator(); - openWithAction_ = new QAction(tr("Other Applications"), this); - connect(openWithAction_, &QAction::triggered, this, &FileMenu::onOpenWithTriggered); - menu->addAction(openWithAction_); - - separator1_ = addSeparator(); - - createAction_ = new QAction(tr("Create &New"), this); - Fm::FilePath dirPath = files_.size() == 1 && info_->isDir() ? path : cwd_; - createAction_->setMenu(new CreateNewMenu(nullptr, dirPath, this)); - addAction(createAction_); - - separator2_ = addSeparator(); - - if(allTrash_) { // all selected files are in trash:/// - bool can_restore = true; - /* only immediate children of trash:/// can be restored. */ - auto trash_root = Fm::FilePath::fromUri("trash:///"); - for(auto& file: files_) { - Fm::FilePath trash_path = file->path(); - if(!trash_root.isParentOf(trash_path)) { - can_restore = false; - break; - } - } - if(can_restore) { - unTrashAction_ = new QAction(tr("&Restore"), this); - connect(unTrashAction_, &QAction::triggered, this, &FileMenu::onUnTrashTriggered); - addAction(unTrashAction_); - } - } - else { // ordinary files - cutAction_ = new QAction(QIcon::fromTheme("edit-cut"), tr("Cut"), this); - connect(cutAction_, &QAction::triggered, this, &FileMenu::onCutTriggered); - addAction(cutAction_); - - copyAction_ = new QAction(QIcon::fromTheme("edit-copy"), tr("Copy"), this); - connect(copyAction_, &QAction::triggered, this, &FileMenu::onCopyTriggered); - addAction(copyAction_); - - pasteAction_ = new QAction(QIcon::fromTheme("edit-paste"), tr("Paste"), this); - connect(pasteAction_, &QAction::triggered, this, &FileMenu::onPasteTriggered); - addAction(pasteAction_); - - deleteAction_ = new QAction(QIcon::fromTheme("user-trash"), tr("&Move to Trash"), this); - connect(deleteAction_, &QAction::triggered, this, &FileMenu::onDeleteTriggered); - addAction(deleteAction_); - - renameAction_ = new QAction(tr("Rename"), this); - connect(renameAction_, &QAction::triggered, this, &FileMenu::onRenameTriggered); - addAction(renameAction_); - - // disable actons that can't be used - bool hasAccessible(false); - bool hasDeletable(false); - bool hasRenamable(false); - for(auto& file: files_) { - if(file->isAccessible()) { - hasAccessible = true; - } - if(file->isDeletable()) { - hasDeletable = true; - } - if(file->canSetName()) { - hasRenamable = true; - } - if (hasAccessible && hasDeletable && hasRenamable) { - break; - } - } - copyAction_->setEnabled(hasAccessible); - cutAction_->setEnabled(hasDeletable); - deleteAction_->setEnabled(hasDeletable); - renameAction_->setEnabled(hasRenamable); - if(!(sameType_ && info_->isDir() - && (files_.size() > 1 ? isWritableDir : info_->isWritable()))) { - pasteAction_->setEnabled(false); - } - } - - // DES-EMA custom actions integration - // FIXME: port these parts to Fm API - auto custom_actions = FileActionItem::get_actions_for_files(files_); - for(auto& item: custom_actions) { - if(item && !(item->get_target() & FILE_ACTION_TARGET_CONTEXT)) { - continue; // this item is not for context menu - } - if(item == custom_actions.front() && !item->is_action()) { - addSeparator(); // before all custom actions - } - addCustomActionItem(this, item); - } - - // archiver integration - // FIXME: we need to modify upstream libfm to include some Qt-based archiver programs. - if(!allVirtual_) { - if(sameType_) { - auto archiver = Archiver::defaultArchiver(); - if(archiver) { - if(archiver->isMimeTypeSupported(mime_type->name())) { - QAction* archiverSeparator = nullptr; - if(cwd_ && archiver->canExtractArchivesTo()) { - archiverSeparator = addSeparator(); - QAction* action = new QAction(tr("Extract to..."), this); - connect(action, &QAction::triggered, this, &FileMenu::onExtract); - addAction(action); - } - if(archiver->canExtractArchives()) { - if(!archiverSeparator) { - addSeparator(); - } - QAction* action = new QAction(tr("Extract Here"), this); - connect(action, &QAction::triggered, this, &FileMenu::onExtractHere); - addAction(action); - } - } - else if(archiver->canCreateArchive()){ - addSeparator(); - QAction* action = new QAction(tr("Compress"), this); - connect(action, &QAction::triggered, this, &FileMenu::onCompress); - addAction(action); - } - } - } - } - - separator3_ = addSeparator(); - - propertiesAction_ = new QAction(QIcon::fromTheme("document-properties"), tr("Properties"), this); - connect(propertiesAction_, &QAction::triggered, this, &FileMenu::onFilePropertiesTriggered); - addAction(propertiesAction_); -} - -FileMenu::~FileMenu() { -} - - -void FileMenu::addCustomActionItem(QMenu* menu, std::shared_ptr item) { - if(!item) { // separator - addSeparator(); - return; - } - - // this action is not for context menu - if(item->is_action() && !(item->get_target() & FILE_ACTION_TARGET_CONTEXT)) { - return; - } - - CustomAction* action = new CustomAction(item, menu); - menu->addAction(action); - if(item->is_menu()) { - auto& subitems = item->get_sub_items(); - if(!subitems.empty()) { - QMenu* submenu = new QMenu(menu); - for(auto& subitem: subitems) { - addCustomActionItem(submenu, subitem); - } - action->setMenu(submenu); - } - } - else if(item->is_action()) { - connect(action, &QAction::triggered, this, &FileMenu::onCustomActionTrigerred); - } -} - -void FileMenu::onOpenTriggered() { - if(fileLauncher_) { - fileLauncher_->launchFiles(nullptr, files_); - } - else { // use the default launcher - Fm::FileLauncher launcher; - launcher.launchFiles(nullptr, files_); - } -} - -void FileMenu::onOpenWithTriggered() { - AppChooserDialog dlg(nullptr); - if(sameType_) { - dlg.setMimeType(info_->mimeType()); - } - else { // we can only set the selected app as default if all files are of the same type - dlg.setCanSetDefault(false); - } - - if(execModelessDialog(&dlg) == QDialog::Accepted) { - auto app = dlg.selectedApp(); - if(app) { - openFilesWithApp(app.get()); - } - } -} - -void FileMenu::openFilesWithApp(GAppInfo* app) { - GList* uris = nullptr; - for(auto& file: files_) { - auto uri = file->path().uri(); - uris = g_list_prepend(uris, uri.release()); - } - fm_app_info_launch_uris(app, uris, nullptr, nullptr); - g_list_foreach(uris, (GFunc)g_free, nullptr); - g_list_free(uris); -} - -void FileMenu::onApplicationTriggered() { - AppInfoAction* action = static_cast(sender()); - openFilesWithApp(action->appInfo().get()); -} - -void FileMenu::onCustomActionTrigerred() { - CustomAction* action = static_cast(sender()); - auto& item = action->item(); - /* g_debug("item: %s is activated, id:%s", fm_file_action_item_get_name(item), - fm_file_action_item_get_id(item)); */ - CStrPtr output; - item->launch(nullptr, files_, output); - if(output) { - QMessageBox::information(this, tr("Output"), output.get()); - } -} - -void FileMenu::onFilePropertiesTriggered() { - FilePropsDialog::showForFiles(files_); -} - -void FileMenu::onCopyTriggered() { - Fm::copyFilesToClipboard(files_.paths()); -} - -void FileMenu::onCutTriggered() { - Fm::cutFilesToClipboard(files_.paths()); -} - -void FileMenu::onDeleteTriggered() { - auto paths = files_.paths(); - if(useTrash_) { - FileOperation::trashFiles(paths, confirmTrash_); - } - else { - FileOperation::deleteFiles(paths, confirmDelete_); - } -} - -void FileMenu::onUnTrashTriggered() { - FileOperation::unTrashFiles(files_.paths()); -} - -void FileMenu::onPasteTriggered() { - Fm::pasteFilesFromClipboard(cwd_); -} - -void FileMenu::onRenameTriggered() { - // if there is a view and this is a single file, just edit the current index - if(files_.size() == 1) { - if (QAbstractItemView* view = qobject_cast(parentWidget())) { - QModelIndexList selIndexes = view->selectionModel()->selectedIndexes(); - if(selIndexes.size() > 1) { // in the detailed list mode, only the first index is editable - view->setCurrentIndex(selIndexes.at(0)); - } - if (view->currentIndex().isValid()) { - view->edit(view->currentIndex()); - return; - } - } - } - for(auto& info: files_) { - if(!Fm::renameFile(info, nullptr)) { - break; - } - } -} - -void FileMenu::setUseTrash(bool trash) { - if(useTrash_ != trash) { - useTrash_ = trash; - if(deleteAction_) { - deleteAction_->setText(useTrash_ ? tr("&Move to Trash") : tr("&Delete")); - deleteAction_->setIcon(useTrash_ ? QIcon::fromTheme("user-trash") : QIcon::fromTheme("edit-delete")); - } - } -} - -void FileMenu::onCompress() { - auto archiver = Archiver::defaultArchiver(); - if(archiver) { - archiver->createArchive(nullptr, files_.paths()); - } -} - -void FileMenu::onExtract() { - auto archiver = Archiver::defaultArchiver(); - if(archiver) { - archiver->extractArchives(nullptr, files_.paths()); - } -} - -void FileMenu::onExtractHere() { - auto archiver = Archiver::defaultArchiver(); - if(archiver) { - archiver->extractArchivesTo(nullptr, files_.paths(), cwd_); - } -} - -} // namespace Fm diff --git a/src/filemenu.h b/src/filemenu.h deleted file mode 100644 index c683d84..0000000 --- a/src/filemenu.h +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_FILEMENU_H -#define FM_FILEMENU_H - -#include "libfmqtglobals.h" -#include -#include -#include -#include "core/fileinfo.h" - -class QAction; - -namespace Fm { - -class FileLauncher; -class FileActionItem; - -class LIBFM_QT_API FileMenu : public QMenu { - Q_OBJECT - -public: - explicit FileMenu(Fm::FileInfoList files, std::shared_ptr info, Fm::FilePath cwd, bool isWritableDir = true, const QString& title = QString(), QWidget* parent = nullptr); - ~FileMenu(); - - bool useTrash() { - return useTrash_; - } - - void setUseTrash(bool trash); - - bool confirmDelete() { - return confirmDelete_; - } - - void setConfirmDelete(bool confirm) { - confirmDelete_ = confirm; - } - - QAction* openAction() { - return openAction_; - } - - QAction* openWithMenuAction() { - return openWithMenuAction_; - } - - QAction* openWithAction() { - return openWithAction_; - } - - QAction* separator1() { - return separator1_; - } - - QAction* createAction() { - return createAction_; - } - - QAction* separator2() { - return separator2_; - } - - QAction* cutAction() { - return cutAction_; - } - - QAction* copyAction() { - return copyAction_; - } - - QAction* pasteAction() { - return pasteAction_; - } - - QAction* deleteAction() { - return deleteAction_; - } - - QAction* unTrashAction() { - return unTrashAction_; - } - - QAction* renameAction() { - return renameAction_; - } - - QAction* separator3() { - return separator3_; - } - - QAction* propertiesAction() { - return propertiesAction_; - } - - const Fm::FileInfoList& files() const { - return files_; - } - - const std::shared_ptr& firstFile() const { - return info_; - } - - const Fm::FilePath& cwd() const { - return cwd_; - } - - void setFileLauncher(FileLauncher* launcher) { - fileLauncher_ = launcher; - } - - FileLauncher* fileLauncher() { - return fileLauncher_; - } - - bool sameType() const { - return sameType_; - } - - bool sameFilesystem() const { - return sameFilesystem_; - } - - bool allVirtual() const { - return allVirtual_; - } - - bool allTrash() const { - return allTrash_; - } - - bool confirmTrash() const { - return confirmTrash_; - } - - void setConfirmTrash(bool value) { - confirmTrash_ = value; - } - -protected: - void addCustomActionItem(QMenu* menu, std::shared_ptr item); - void openFilesWithApp(GAppInfo* app); - -protected Q_SLOTS: - void onOpenTriggered(); - void onOpenWithTriggered(); - void onFilePropertiesTriggered(); - void onApplicationTriggered(); - void onCustomActionTrigerred(); - void onCompress(); - void onExtract(); - void onExtractHere(); - - void onCutTriggered(); - void onCopyTriggered(); - void onPasteTriggered(); - void onRenameTriggered(); - void onDeleteTriggered(); - void onUnTrashTriggered(); - -private: - Fm::FileInfoList files_; - std::shared_ptr info_; - Fm::FilePath cwd_; - bool useTrash_; - bool confirmDelete_; - bool confirmTrash_; // Confirm before moving files into "trash can" - - bool sameType_; - bool sameFilesystem_; - bool allVirtual_; - bool allTrash_; - - QAction* openAction_; - QAction* openWithMenuAction_; - QAction* openWithAction_; - QAction* separator1_; - QAction* createAction_; - QAction* separator2_; - QAction* cutAction_; - QAction* copyAction_; - QAction* pasteAction_; - QAction* deleteAction_; - QAction* unTrashAction_; - QAction* renameAction_; - QAction* separator3_; - QAction* propertiesAction_; - - FileLauncher* fileLauncher_; -}; - -} - -#endif // FM_FILEMENU_H diff --git a/src/filemenu_p.h b/src/filemenu_p.h deleted file mode 100644 index 9cbd6ee..0000000 --- a/src/filemenu_p.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_FILEMENU_P_H -#define FM_FILEMENU_P_H - -#include -#include "core/gioptrs.h" -#include "core/iconinfo.h" - -namespace Fm { - -class AppInfoAction : public QAction { - Q_OBJECT -public: - explicit AppInfoAction(Fm::GAppInfoPtr app, QObject* parent = 0): - QAction(QString::fromUtf8(g_app_info_get_name(app.get())), parent), - appInfo_{std::move(app)} { - setToolTip(QString::fromUtf8(g_app_info_get_description(appInfo_.get()))); - GIcon* gicon = g_app_info_get_icon(appInfo_.get()); - const auto icnInfo = Fm::IconInfo::fromGIcon(gicon); - if(icnInfo) { - setIcon(icnInfo->qicon()); - } - } - - virtual ~AppInfoAction() { - } - - const Fm::GAppInfoPtr& appInfo() const { - return appInfo_; - } - -private: - Fm::GAppInfoPtr appInfo_; -}; - -} // namespace Fm - -#endif diff --git a/src/fileoperation.cpp b/src/fileoperation.cpp deleted file mode 100644 index 1e5d582..0000000 --- a/src/fileoperation.cpp +++ /dev/null @@ -1,422 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 "fileoperation.h" -#include "fileoperationdialog.h" -#include -#include -#include -#include - -#include "core/deletejob.h" -#include "core/trashjob.h" -#include "core/untrashjob.h" -#include "core/filetransferjob.h" -#include "core/filechangeattrjob.h" -#include "utilities.h" - -namespace Fm { - -#define SHOW_DLG_DELAY 1000 - -FileOperation::FileOperation(Type type, Fm::FilePathList srcPaths, QObject* parent): - QObject(parent), - type_{type}, - job_{nullptr}, - dlg_{nullptr}, - srcPaths_{std::move(srcPaths)}, - uiTimer_(nullptr), - elapsedTimer_(nullptr), - lastElapsed_(0), - updateRemainingTime_(true), - autoDestroy_(true) { - - switch(type_) { - case Copy: - job_ = new FileTransferJob(srcPaths_, FileTransferJob::Mode::COPY); - break; - case Move: - job_ = new FileTransferJob(srcPaths_, FileTransferJob::Mode::MOVE); - break; - case Link: - job_ = new FileTransferJob(srcPaths_, FileTransferJob::Mode::LINK); - break; - case Delete: - job_ = new Fm::DeleteJob(srcPaths_); - break; - case Trash: - job_ = new Fm::TrashJob(srcPaths_); - break; - case UnTrash: - job_ = new Fm::UntrashJob(srcPaths_); - break; - case ChangeAttr: - job_ = new Fm::FileChangeAttrJob(srcPaths_); - break; - default: - break; - } - - if(job_) { - // automatically delete the job object when it's finished. - job_->setAutoDelete(true); - - // new C++ jobs - connect(job_, &Fm::Job::finished, this, &Fm::FileOperation::onJobFinish); - connect(job_, &Fm::Job::cancelled, this, &Fm::FileOperation::onJobCancalled); - connect(job_, &Fm::Job::error, this, &Fm::FileOperation::onJobError, Qt::BlockingQueuedConnection); - connect(job_, &Fm::FileOperationJob::fileExists, this, &Fm::FileOperation::onJobFileExists, Qt::BlockingQueuedConnection); - - // we block the job deliberately until we prepare to start (initiailize the timer) so we can calculate elapsed time correctly. - connect(job_, &Fm::FileOperationJob::preparedToRun, this, &Fm::FileOperation::onJobPrepared, Qt::BlockingQueuedConnection); - } -} - -void FileOperation::disconnectJob() { - if(job_) { - disconnect(job_, &Fm::Job::finished, this, &Fm::FileOperation::onJobFinish); - disconnect(job_, &Fm::Job::cancelled, this, &Fm::FileOperation::onJobCancalled); - disconnect(job_, &Fm::Job::error, this, &Fm::FileOperation::onJobError); - disconnect(job_, &Fm::FileOperationJob::fileExists, this, &Fm::FileOperation::onJobFileExists); - disconnect(job_, &Fm::FileOperationJob::preparedToRun, this, &Fm::FileOperation::onJobPrepared); - } -} - -FileOperation::~FileOperation() { - if(uiTimer_) { - uiTimer_->stop(); - delete uiTimer_; - uiTimer_ = nullptr; - } - if(elapsedTimer_) { - delete elapsedTimer_; - elapsedTimer_ = nullptr; - } -} - -void FileOperation::setDestination(Fm::FilePath dest) { - destPath_ = std::move(dest); - switch(type_) { - case Copy: - case Move: - case Link: - if(job_) { - static_cast(job_)->setDestDirPath(destPath_); - } - break; - default: - break; - } -} - -void FileOperation::setDestFiles(FilePathList destFiles) { - switch(type_) { - case Copy: - case Move: - case Link: - if(job_) { - static_cast(job_)->setDestPaths(std::move(destFiles)); - } - break; - default: - break; - } -} - -void FileOperation::setChmod(mode_t newMode, mode_t newModeMask) { - if(job_) { - auto job = static_cast(job_); - job->setFileModeEnabled(true); - job->setFileMode(newMode, newModeMask); - } -} - -void FileOperation::setChown(uid_t uid, gid_t gid) { - if(job_) { - auto job = static_cast(job_); - if(uid != INVALID_UID) { - job->setOwnerEnabled(true); - job->setOwner(uid); - } - if(gid != INVALID_GID) { - job->setGroupEnabled(true); - job->setGroup(gid); - } - } -} - -void FileOperation::setRecursiveChattr(bool recursive) { - if(job_) { - auto job = static_cast(job_); - job->setRecursive(recursive); - } -} - -bool FileOperation::run() { - delete uiTimer_; - // run the job - uiTimer_ = new QTimer(); - uiTimer_->start(SHOW_DLG_DELAY); - connect(uiTimer_, &QTimer::timeout, this, &FileOperation::onUiTimeout); - - if(job_) { - job_->runAsync(); - return true; - } - return false; -} - -void FileOperation::cancel() { - if(job_) { - job_->cancel(); - } -} - -void FileOperation::onUiTimeout() { - if(dlg_) { - // estimate remaining time based on past history - if(job_) { - Fm::FilePath curFilePath = job_->currentFile(); - // update progress bar - double finishedRatio = job_->progress(); - if(finishedRatio > 0.0 && updateRemainingTime_) { - dlg_->setPercent(int(finishedRatio * 100)); - - std::uint64_t totalSize, totalCount, finishedSize, finishedCount; - job_->totalAmount(totalSize, totalCount); - job_->finishedAmount(finishedSize, finishedCount); - - // only show data transferred if the job progress can be calculated by file size. - // for jobs not related to data transfer (for example: change attr, delete,...), hide the UI - if(job_->calcProgressUsingSize()) { - dlg_->setDataTransferred(finishedSize, totalSize); - } - else { - dlg_->setFilesProcessed(finishedCount, totalCount); - } - - double remainRatio = 1.0 - finishedRatio; - gint64 remaining = elapsedTime() * (remainRatio / finishedRatio) / 1000; - // qDebug("etime: %llu, finished: %lf, remain:%lf, remaining secs: %llu", - // elapsedTime(), finishedRatio, remainRatio, remaining); - dlg_->setRemainingTime(remaining); - } - // update currently processed file - if(curFilePath_ != curFilePath) { - curFilePath_ = std::move(curFilePath); - // FIXME: make this cleaner - curFile = QString::fromUtf8(curFilePath_.toString().get()); - dlg_->setCurFile(curFile); - } - } - // this timeout slot is called every 0.5 second. - // by adding this flag, we can update remaining time every 1 second. - updateRemainingTime_ = !updateRemainingTime_; - } - else { - showDialog(); - } -} - -void FileOperation::showDialog() { - if(!dlg_) { - dlg_ = new FileOperationDialog(this); - dlg_->setSourceFiles(srcPaths_); - - if(destPath_) { - dlg_->setDestPath(destPath_); - } - - if(curFile.isEmpty()) { - dlg_->setPrepared(); - dlg_->setCurFile(curFile); - } - uiTimer_->setInterval(500); // change the interval of the timer - // now the timer is used to update current file display - dlg_->show(); - } -} - -void FileOperation::onJobFileExists(const FileInfo& src, const FileInfo& dest, Fm::FileOperationJob::FileExistsAction& response, FilePath& newDest) { - pauseElapsedTimer(); - showDialog(); - response = dlg_->askRename(src, dest, newDest); - resumeElapsedTimer(); -} - -void FileOperation::onJobCancalled() { - qDebug("file operation is cancelled!"); -} - -void FileOperation::onJobError(const GErrorPtr& err, Fm::Job::ErrorSeverity severity, Fm::Job::ErrorAction& response) { - pauseElapsedTimer(); - showDialog(); - response = Fm::Job::ErrorAction(dlg_->error(err.get(), severity)); - resumeElapsedTimer(); -} - -void FileOperation::onJobPrepared() { - if(!elapsedTimer_) { - elapsedTimer_ = new QElapsedTimer(); - elapsedTimer_->start(); - } - if(dlg_) { - dlg_->setPrepared(); - } -} - -void FileOperation::onJobFinish() { - disconnectJob(); - - if(uiTimer_) { - uiTimer_->stop(); - delete uiTimer_; - uiTimer_ = nullptr; - } - - if(dlg_) { - dlg_->done(QDialog::Accepted); - delete dlg_; - dlg_ = nullptr; - } - Q_EMIT finished(); - - // special handling for trash job - if(type_ == Trash && !job_->isCancelled()) { - auto trashJob = static_cast(job_); - /* some files cannot be trashed because underlying filesystems don't support it. */ - auto unsupportedFiles = trashJob->unsupportedFiles(); - if(!unsupportedFiles.empty()) { /* delete them instead */ - /* FIXME: parent window might be already destroyed! */ - QWidget* parent = nullptr; // FIXME: currently, parent window is not set - if(QMessageBox::question(parent, tr("Error"), - tr("Some files cannot be moved to trash can because " - "the underlying file systems don't support this operation.\n" - "Do you want to delete them instead?")) == QMessageBox::Yes) { - deleteFiles(std::move(unsupportedFiles), false); - } - } - } - - if(autoDestroy_) { - delete this; - } -} - -// static -FileOperation* FileOperation::copyFiles(Fm::FilePathList srcFiles, Fm::FilePath dest, QWidget* parent) { - FileOperation* op = new FileOperation(FileOperation::Copy, std::move(srcFiles), parent); - op->setDestination(dest); - op->run(); - return op; -} - -//static -FileOperation *FileOperation::copyFiles(FilePathList srcFiles, FilePathList destFiles, QWidget *parent) { - qDebug("copy: %s -> %s", srcFiles[0].toString().get(), destFiles[0].toString().get()); - FileOperation* op = new FileOperation(FileOperation::Copy, std::move(srcFiles), parent); - op->setDestFiles(std::move(destFiles)); - op->run(); - return op; -} - -// static -FileOperation* FileOperation::moveFiles(Fm::FilePathList srcFiles, Fm::FilePath dest, QWidget* parent) { - FileOperation* op = new FileOperation(FileOperation::Move, std::move(srcFiles), parent); - op->setDestination(dest); - op->run(); - return op; -} - -//static -FileOperation *FileOperation::moveFiles(FilePathList srcFiles, FilePathList destFiles, QWidget *parent) { - FileOperation* op = new FileOperation(FileOperation::Move, std::move(srcFiles), parent); - op->setDestFiles(std::move(destFiles)); - op->run(); - return op; -} - -//static -FileOperation* FileOperation::symlinkFiles(Fm::FilePathList srcFiles, Fm::FilePath dest, QWidget* parent) { - FileOperation* op = new FileOperation(FileOperation::Link, std::move(srcFiles), parent); - op->setDestination(dest); - op->run(); - return op; -} - -//static -FileOperation *FileOperation::symlinkFiles(FilePathList srcFiles, FilePathList destFiles, QWidget *parent) { - FileOperation* op = new FileOperation(FileOperation::Link, std::move(srcFiles), parent); - op->setDestFiles(std::move(destFiles)); - op->run(); - return op; -} - -//static -FileOperation* FileOperation::deleteFiles(Fm::FilePathList srcFiles, bool prompt, QWidget* parent) { - if(prompt) { - int result = QMessageBox::warning(parent, tr("Confirm"), - tr("Do you want to delete the selected files?"), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No); - if(result != QMessageBox::Yes) { - return nullptr; - } - } - - FileOperation* op = new FileOperation(FileOperation::Delete, std::move(srcFiles)); - op->run(); - return op; -} - -//static -FileOperation* FileOperation::trashFiles(Fm::FilePathList srcFiles, bool prompt, QWidget* parent) { - if(prompt) { - int result = QMessageBox::warning(parent, tr("Confirm"), - tr("Do you want to move the selected files to trash can?"), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No); - if(result != QMessageBox::Yes) { - return nullptr; - } - } - - FileOperation* op = new FileOperation(FileOperation::Trash, std::move(srcFiles)); - op->run(); - return op; -} - -//static -FileOperation* FileOperation::unTrashFiles(Fm::FilePathList srcFiles, QWidget* parent) { - FileOperation* op = new FileOperation(FileOperation::UnTrash, std::move(srcFiles), parent); - op->run(); - return op; -} - -// static -FileOperation* FileOperation::changeAttrFiles(Fm::FilePathList srcFiles, QWidget* parent) { - //TODO - FileOperation* op = new FileOperation(FileOperation::ChangeAttr, std::move(srcFiles), parent); - op->run(); - return op; -} - - -} // namespace Fm diff --git a/src/fileoperation.h b/src/fileoperation.h deleted file mode 100644 index 46c5215..0000000 --- a/src/fileoperation.h +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_FILEOPERATION_H -#define FM_FILEOPERATION_H - -#include "libfmqtglobals.h" -#include -#include -#include "core/filepath.h" -#include "core/fileoperationjob.h" - -class QTimer; - -namespace Fm { - -class FileOperationDialog; - -class LIBFM_QT_API FileOperation : public QObject { - Q_OBJECT -public: - enum Type { - Copy, - Move, - Link, - Delete, - Trash, - UnTrash, - ChangeAttr - }; - -public: - explicit FileOperation(Type type, Fm::FilePathList srcFiles, QObject* parent = 0); - - virtual ~FileOperation(); - - void setDestination(Fm::FilePath dest); - - void setDestFiles(FilePathList destFiles); - - void setChmod(mode_t newMode, mode_t newModeMask); - - void setChown(uid_t uid, gid_t gid); - - // This only work for change attr jobs. - void setRecursiveChattr(bool recursive); - - bool run(); - - void cancel(); - - bool isRunning() const { - return job_ && !isCancelled(); - } - - bool isCancelled() const { - if(job_) { - return job_->isCancelled(); - } - return false; - } - - Fm::FileOperationJob* job() { - return job_; - } - - bool autoDestroy() { - return autoDestroy_; - } - void setAutoDestroy(bool destroy = true) { - autoDestroy_ = destroy; - } - - Type type() { - return type_; - } - - // convinient static functions - static FileOperation* copyFiles(Fm::FilePathList srcFiles, Fm::FilePath dest, QWidget* parent = 0); - - static FileOperation* copyFiles(Fm::FilePathList srcFiles, Fm::FilePathList destFiles, QWidget* parent = 0); - - static FileOperation* copyFile(Fm::FilePath srcFile, Fm::FilePath destFile, QWidget* parent = 0) { - return copyFiles(FilePathList{std::move(srcFile)}, FilePathList{std::move(destFile)}, parent); - } - - static FileOperation* moveFiles(Fm::FilePathList srcFiles, Fm::FilePath dest, QWidget* parent = 0); - - static FileOperation* moveFiles(Fm::FilePathList srcFiles, Fm::FilePathList destFiles, QWidget* parent = 0); - - static FileOperation* moveFile(Fm::FilePath srcFile, Fm::FilePath destFile, QWidget* parent = 0) { - return moveFiles(FilePathList{std::move(srcFile)}, FilePathList{std::move(destFile)}, parent); - } - - static FileOperation* symlinkFiles(Fm::FilePathList srcFiles, Fm::FilePath dest, QWidget* parent = 0); - - static FileOperation* symlinkFiles(Fm::FilePathList srcFiles, Fm::FilePathList destFiles, QWidget* parent = 0); - - static FileOperation* symlinkFile(Fm::FilePath srcFile, Fm::FilePath destFile, QWidget* parent = 0) { - return symlinkFiles(FilePathList{std::move(srcFile)}, FilePathList{std::move(destFile)}, parent); - } - - static FileOperation* deleteFiles(Fm::FilePathList srcFiles, bool promp = true, QWidget* parent = 0); - - static FileOperation* trashFiles(Fm::FilePathList srcFiles, bool promp = true, QWidget* parent = 0); - - static FileOperation* unTrashFiles(Fm::FilePathList srcFiles, QWidget* parent = 0); - - static FileOperation* changeAttrFiles(Fm::FilePathList srcFiles, QWidget* parent = 0); - -Q_SIGNALS: - void finished(); - -private Q_SLOTS: - void onJobPrepared(); - void onJobFinish(); - void onJobCancalled(); - void onJobError(const GErrorPtr& err, Fm::Job::ErrorSeverity severity, Fm::Job::ErrorAction& response); - void onJobFileExists(const FileInfo& src, const FileInfo& dest, Fm::FileOperationJob::FileExistsAction& response, FilePath& newDest); - -private: - - void disconnectJob(); - void showDialog(); - - void pauseElapsedTimer() { - if(Q_LIKELY(elapsedTimer_ != nullptr)) { - lastElapsed_ += elapsedTimer_->elapsed(); - elapsedTimer_->invalidate(); - } - } - - void resumeElapsedTimer() { - if(Q_LIKELY(elapsedTimer_ != nullptr)) { - elapsedTimer_->start(); - } - } - - qint64 elapsedTime() { - if(Q_LIKELY(elapsedTimer_ != nullptr)) { - return lastElapsed_ + elapsedTimer_->elapsed(); - } - return 0; - } - -private Q_SLOTS: - void onUiTimeout(); - -private: - Type type_; - FileOperationJob* job_; - FileOperationDialog* dlg_; - FilePath destPath_; - FilePath curFilePath_; - FilePathList srcPaths_; - QTimer* uiTimer_; - QElapsedTimer* elapsedTimer_; - qint64 lastElapsed_; - bool updateRemainingTime_; - QString curFile; - bool autoDestroy_; -}; - -} - -#endif // FM_FILEOPERATION_H diff --git a/src/fileoperationdialog.cpp b/src/fileoperationdialog.cpp deleted file mode 100644 index 73f80fd..0000000 --- a/src/fileoperationdialog.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 "fileoperationdialog.h" -#include "fileoperation.h" -#include "renamedialog.h" -#include -#include -#include -#include "utilities.h" -#include "ui_file-operation-dialog.h" - - -namespace Fm { - -FileOperationDialog::FileOperationDialog(FileOperation* _operation): - QDialog(nullptr), - operation(_operation), - defaultOption(-1), - ignoreNonCriticalErrors_(false) { - - ui = new Ui::FileOperationDialog(); - ui->setupUi(this); - - QString title; - QString message; - switch(_operation->type()) { - case FileOperation::Move: - title = tr("Move files"); - message = tr("Moving the following files to destination folder:"); - break; - case FileOperation::Copy: - title = tr("Copy Files"); - message = tr("Copying the following files to destination folder:"); - break; - case FileOperation::Trash: - title = tr("Trash Files"); - message = tr("Moving the following files to trash can:"); - break; - case FileOperation::Delete: - title = tr("Delete Files"); - message = tr("Deleting the following files:"); - ui->dest->hide(); - ui->destLabel->hide(); - break; - case FileOperation::Link: - title = tr("Create Symlinks"); - message = tr("Creating symlinks for the following files:"); - break; - case FileOperation::ChangeAttr: - title = tr("Change Attributes"); - message = tr("Changing attributes of the following files:"); - ui->dest->hide(); - ui->destLabel->hide(); - break; - case FileOperation::UnTrash: - title = tr("Restore Trashed Files"); - message = tr("Restoring the following files from trash can:"); - ui->dest->hide(); - ui->destLabel->hide(); - break; - } - ui->message->setText(message); - setWindowTitle(title); -} - - -FileOperationDialog::~FileOperationDialog() { - delete ui; -} - -void FileOperationDialog::setDestPath(const Fm::FilePath &dest) { - ui->dest->setText(dest.displayName().get()); -} - -void FileOperationDialog::setSourceFiles(const Fm::FilePathList &srcFiles) { - for(auto& srcFile : srcFiles) { - ui->sourceFiles->addItem(srcFile.displayName().get()); - } -} - -int FileOperationDialog::ask(QString /*question*/, char* const* /*options*/) { - // TODO: implement FileOperationDialog::ask() - return 0; -} - - -FileOperationJob::FileExistsAction FileOperationDialog::askRename(const FileInfo &src, const FileInfo &dest, FilePath &newDest) { - FileOperationJob::FileExistsAction ret; - if(defaultOption == -1) { // default action is not set, ask the user - RenameDialog dlg(src, dest, this); - dlg.exec(); - switch(dlg.action()) { - case RenameDialog::ActionOverwrite: - ret = FileOperationJob::OVERWRITE; - if(dlg.applyToAll()) { - defaultOption = ret; - } - break; - case RenameDialog::ActionRename: { - ret = FileOperationJob::RENAME; - auto newName = dlg.newName(); - if(!newName.isEmpty()) { - auto destDirPath = dest.path().parent(); - newDest = destDirPath.child(newName.toUtf8().constData()); - } - break; - } - case RenameDialog::ActionIgnore: - ret = FileOperationJob::SKIP; - if(dlg.applyToAll()) { - defaultOption = ret; - } - break; - default: - ret = FileOperationJob::CANCEL; - break; - } - } - else { - ret = (FileOperationJob::FileExistsAction)defaultOption; - } - return ret; -} - -Job::ErrorAction FileOperationDialog::error(GError* err, Job::ErrorSeverity severity) { - if(severity >= Job::ErrorSeverity::MODERATE) { - if(severity == Job::ErrorSeverity::CRITICAL) { - QMessageBox::critical(this, tr("Error"), QString::fromUtf8(err->message)); - return Job::ErrorAction::ABORT; - } - if (ignoreNonCriticalErrors_) { - return Job::ErrorAction::CONTINUE; - } - QMessageBox::StandardButton stb = QMessageBox::critical(this, tr("Error"), QString::fromUtf8(err->message), - QMessageBox::Ok | QMessageBox::Ignore); - if (stb == QMessageBox::Ignore) { - ignoreNonCriticalErrors_ = true; - } - } - return Job::ErrorAction::CONTINUE; -} - -void FileOperationDialog::setCurFile(QString cur_file) { - ui->curFile->setText(cur_file); -} - -void FileOperationDialog::setDataTransferred(uint64_t finishedSize, std::uint64_t totalSize) { - ui->filesProcessed->setText(QString("%1 / %2") - .arg(formatFileSize(finishedSize, fm_config->si_unit)) - .arg(formatFileSize(totalSize, fm_config->si_unit))); -} - -void FileOperationDialog::setFilesProcessed(uint64_t finishedCount, uint64_t totalCount) { - ui->filesProcessed->setText(QString("%1 / %2") - .arg(finishedCount) - .arg(totalCount)); -} - -void FileOperationDialog::setPercent(unsigned int percent) { - ui->progressBar->setValue(percent); -} - - -void FileOperationDialog::setRemainingTime(unsigned int sec) { - unsigned int min = 0; - unsigned int hr = 0; - if(sec > 60) { - min = sec / 60; - sec %= 60; - if(min > 60) { - hr = min / 60; - min %= 60; - } - } - ui->timeRemaining->setText(QString("%1:%2:%3") - .arg(hr, 2, 10, QChar('0')) - .arg(min, 2, 10, QChar('0')) - .arg(sec, 2, 10, QChar('0'))); -} - -void FileOperationDialog::setPrepared() { -} - -void FileOperationDialog::reject() { - operation->cancel(); - QDialog::reject(); -} - - -} // namespace Fm diff --git a/src/fileoperationdialog.h b/src/fileoperationdialog.h deleted file mode 100644 index 47cfe1c..0000000 --- a/src/fileoperationdialog.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - - -#ifndef FM_FILEOPERATIONDIALOG_H -#define FM_FILEOPERATIONDIALOG_H - -#include "libfmqtglobals.h" -#include -#include -#include -#include "core/filepath.h" -#include "core/fileinfo.h" -#include "core/fileoperationjob.h" - -namespace Ui { -class FileOperationDialog; -} - -namespace Fm { - -class FileOperation; - -class LIBFM_QT_API FileOperationDialog : public QDialog { - Q_OBJECT -public: - explicit FileOperationDialog(FileOperation* _operation); - virtual ~FileOperationDialog(); - - void setSourceFiles(const Fm::FilePathList& srcFiles); - void setDestPath(const Fm::FilePath& dest); - - int ask(QString question, char* const* options); - - FileOperationJob::FileExistsAction askRename(const FileInfo& src, const FileInfo& dest, FilePath& newDest); - - Job::ErrorAction error(GError* err, Job::ErrorSeverity severity); - void setPrepared(); - void setCurFile(QString cur_file); - void setPercent(unsigned int percent); - void setDataTransferred(std::uint64_t finishedSize, std::uint64_t totalSize); - void setFilesProcessed(std::uint64_t finishedCount, std::uint64_t totalCount); - void setRemainingTime(unsigned int sec); - - virtual void reject(); - -private: - Ui::FileOperationDialog* ui; - FileOperation* operation; - int defaultOption; - bool ignoreNonCriticalErrors_; -}; - -} - -#endif // FM_FILEOPERATIONDIALOG_H diff --git a/src/fileoperationdialog_p.h b/src/fileoperationdialog_p.h deleted file mode 100644 index a73f3f4..0000000 --- a/src/fileoperationdialog_p.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - - -#ifndef FM_FILEOPERATIONDIALOG_P_H -#define FM_FILEOPERATIONDIALOG_P_H - -#include -#include -#include - -namespace Fm { - -class ElidedLabel : public QLabel { -Q_OBJECT - -public: - explicit ElidedLabel(QWidget *parent = 0, Qt::WindowFlags f = Qt::WindowFlags()): - QLabel(parent, f), - lastWidth_(0) { - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - // set a min width to prevent the window from widening with long texts - setMinimumWidth(fontMetrics().averageCharWidth() * 10); - } - -protected: - // A simplified version of QLabel::paintEvent() without pixmap or shortcut but with eliding. - void paintEvent(QPaintEvent* /*event*/) override { - QRect cr = contentsRect().adjusted(margin(), margin(), -margin(), -margin()); - QString txt = text(); - // if the text is changed or its rect is resized (due to window resizing), - // find whether it needs to be elided... - if (txt != lastText_ || cr.width() != lastWidth_) { - lastText_ = txt; - lastWidth_ = cr.width(); - elidedText_ = fontMetrics().elidedText(txt, Qt::ElideMiddle, cr.width()); - } - // ... then, draw the (elided) text - if(!elidedText_.isEmpty()) { - QPainter painter(this); - QStyleOption opt; - opt.initFrom(this); - style()->drawItemText(&painter, cr, alignment(), opt.palette, isEnabled(), elidedText_, foregroundRole()); - } - } - -private: - QString elidedText_; - QString lastText_; - int lastWidth_; -}; - -} - -#endif // FM_FILEOPERATIONDIALOG_P_H diff --git a/src/filepropsdialog.cpp b/src/filepropsdialog.cpp deleted file mode 100644 index 06500f9..0000000 --- a/src/filepropsdialog.cpp +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 "filepropsdialog.h" -#include "ui_file-props.h" -#include "utilities.h" -#include "fileoperation.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "core/totalsizejob.h" -#include "core/folder.h" - -#define DIFFERENT_UIDS ((uid)-1) -#define DIFFERENT_GIDS ((gid)-1) -#define DIFFERENT_PERMS ((mode_t)-1) - -namespace Fm { - -enum { - ACCESS_NO_CHANGE = 0, - ACCESS_READ_ONLY, - ACCESS_READ_WRITE, - ACCESS_FORBID -}; - -FilePropsDialog::FilePropsDialog(Fm::FileInfoList files, QWidget* parent, Qt::WindowFlags f): - QDialog(parent, f), - fileInfos_{std::move(files)}, - fileInfo{fileInfos_.front()}, - singleType(fileInfos_.isSameType()), - singleFile(fileInfos_.size() == 1 ? true : false) { - - setAttribute(Qt::WA_DeleteOnClose); - - ui = new Ui::FilePropsDialog(); - ui->setupUi(this); - - if(singleType) { - mimeType = fileInfo->mimeType(); - } - - totalSizeJob = new Fm::TotalSizeJob(fileInfos_.paths(), Fm::TotalSizeJob::DEFAULT); - - initGeneralPage(); - initPermissionsPage(); -} - -FilePropsDialog::~FilePropsDialog() { - // Stop the timer if it's still running - if(fileSizeTimer) { - fileSizeTimer->stop(); - delete fileSizeTimer; - fileSizeTimer = nullptr; - } - - // Cancel the indexing job if it hasn't finished - if(totalSizeJob) { - totalSizeJob->cancel(); - totalSizeJob = nullptr; - } - - // And finally delete the dialog's UI - delete ui; -} - -void FilePropsDialog::initApplications() { - if(singleType && mimeType && !fileInfo->isDir()) { - ui->openWith->setMimeType(mimeType); - } - else { - ui->openWith->hide(); - ui->openWithLabel->hide(); - } -} - -void FilePropsDialog::initPermissionsPage() { - // ownership handling - // get owner/group and mode of the first file in the list - uid = fileInfo->uid(); - gid = fileInfo->gid(); - mode_t mode = fileInfo->mode(); - ownerPerm = (mode & (S_IRUSR | S_IWUSR | S_IXUSR)); - groupPerm = (mode & (S_IRGRP | S_IWGRP | S_IXGRP)); - otherPerm = (mode & (S_IROTH | S_IWOTH | S_IXOTH)); - execPerm = (mode & (S_IXUSR | S_IXGRP | S_IXOTH)); - allNative = fileInfo->isNative(); - hasDir = S_ISDIR(mode); - - // check if all selected files belongs to the same owner/group or have the same mode - // at the same time, check if all files are on native unix filesystems - for(auto& fi: fileInfos_) { - if(allNative && !fi->isNative()) { - allNative = false; // not all of the files are native - } - - mode_t fi_mode = fi->mode(); - if(S_ISDIR(fi_mode)) { - hasDir = true; // the files list contains dir(s) - } - - if(uid != DIFFERENT_UIDS && static_cast(uid) != fi->uid()) { - uid = DIFFERENT_UIDS; // not all files have the same owner - } - if(gid != DIFFERENT_GIDS && static_cast(gid) != fi->gid()) { - gid = DIFFERENT_GIDS; // not all files have the same owner group - } - - if(ownerPerm != DIFFERENT_PERMS && ownerPerm != (fi_mode & (S_IRUSR | S_IWUSR | S_IXUSR))) { - ownerPerm = DIFFERENT_PERMS; // not all files have the same permission for owner - } - if(groupPerm != DIFFERENT_PERMS && groupPerm != (fi_mode & (S_IRGRP | S_IWGRP | S_IXGRP))) { - groupPerm = DIFFERENT_PERMS; // not all files have the same permission for grop - } - if(otherPerm != DIFFERENT_PERMS && otherPerm != (fi_mode & (S_IROTH | S_IWOTH | S_IXOTH))) { - otherPerm = DIFFERENT_PERMS; // not all files have the same permission for other - } - if(execPerm != DIFFERENT_PERMS && execPerm != (fi_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { - execPerm = DIFFERENT_PERMS; // not all files have the same executable permission - } - } - - // init owner/group - initOwner(); - - // if all files are of the same type, and some of them are dirs => all of the items are dirs - // rwx values have different meanings for dirs - // Let's make it clear for the users - // init combo boxes for file permissions here - QStringList comboItems; - comboItems.append("---"); // no change - if(singleType && hasDir) { // all files are dirs - comboItems.append(tr("View folder content")); - comboItems.append(tr("View and modify folder content")); - ui->executable->hide(); - } - else { //not all of the files are dirs - comboItems.append(tr("Read")); - comboItems.append(tr("Read and write")); - } - comboItems.append(tr("Forbidden")); - QStringListModel* comboModel = new QStringListModel(comboItems, this); - ui->ownerPerm->setModel(comboModel); - ui->groupPerm->setModel(comboModel); - ui->otherPerm->setModel(comboModel); - - // owner - ownerPermSel = ACCESS_NO_CHANGE; - if(ownerPerm != DIFFERENT_PERMS) { // permissions for owner are the same among all files - if(ownerPerm & S_IRUSR) { // can read - if(ownerPerm & S_IWUSR) { // can write - ownerPermSel = ACCESS_READ_WRITE; - } - else { - ownerPermSel = ACCESS_READ_ONLY; - } - } - else { - if((ownerPerm & S_IWUSR) == 0) { // cannot read or write - ownerPermSel = ACCESS_FORBID; - } - } - } - ui->ownerPerm->setCurrentIndex(ownerPermSel); - - // owner and group - groupPermSel = ACCESS_NO_CHANGE; - if(groupPerm != DIFFERENT_PERMS) { // permissions for owner are the same among all files - if(groupPerm & S_IRGRP) { // can read - if(groupPerm & S_IWGRP) { // can write - groupPermSel = ACCESS_READ_WRITE; - } - else { - groupPermSel = ACCESS_READ_ONLY; - } - } - else { - if((groupPerm & S_IWGRP) == 0) { // cannot read or write - groupPermSel = ACCESS_FORBID; - } - } - } - ui->groupPerm->setCurrentIndex(groupPermSel); - - // other - otherPermSel = ACCESS_NO_CHANGE; - if(otherPerm != DIFFERENT_PERMS) { // permissions for owner are the same among all files - if(otherPerm & S_IROTH) { // can read - if(otherPerm & S_IWOTH) { // can write - otherPermSel = ACCESS_READ_WRITE; - } - else { - otherPermSel = ACCESS_READ_ONLY; - } - } - else { - if((otherPerm & S_IWOTH) == 0) { // cannot read or write - otherPermSel = ACCESS_FORBID; - } - } - - } - ui->otherPerm->setCurrentIndex(otherPermSel); - - // set the checkbox to partially checked state - // when owner, group, and other have different executable flags set. - // some of them have exec, and others do not have. - execCheckState = Qt::PartiallyChecked; - if(execPerm != DIFFERENT_PERMS) { // if all files have the same executable permission - // check if the files are all executable - if((mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == (S_IXUSR | S_IXGRP | S_IXOTH)) { - // owner, group, and other all have exec permission. - ui->executable->setTristate(false); - execCheckState = Qt::Checked; - } - else if((mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) { - // owner, group, and other all have no exec permission - ui->executable->setTristate(false); - execCheckState = Qt::Unchecked; - } - } - ui->executable->setCheckState(execCheckState); -} - -void FilePropsDialog::initGeneralPage() { - // update UI - if(singleType) { // all files are of the same mime-type - std::shared_ptr icon; - // FIXME: handle custom icons for some files - // FIXME: display special property pages for special files or - // some specified mime-types. - if(singleFile) { // only one file is selected. - icon = fileInfo->icon(); - } - if(mimeType) { - if(!icon) { // get an icon from mime type if needed - icon = mimeType->icon(); - } - ui->fileType->setText(mimeType->desc()); - ui->mimeType->setText(mimeType->name()); - } - if(icon) { - ui->iconButton->setIcon(icon->qicon()); - } - - if(singleFile && fileInfo->isSymlink()) { - ui->target->setText(QString::fromStdString(fileInfo->target())); - } - else { - ui->target->hide(); - ui->targetLabel->hide(); - } - if(fileInfo->isDir() && fileInfo->isNative()) { // all files are native dirs - connect(ui->iconButton, &QAbstractButton::clicked, this, &FilePropsDialog::onIconButtonclicked); - } - } // end if(singleType) - else { // not singleType, multiple files are selected at the same time - ui->fileType->setText(tr("Files of different types")); - ui->target->hide(); - ui->targetLabel->hide(); - } - - // FIXME: check if all files has the same parent dir, mtime, or atime - if(singleFile) { // only one file is selected - auto parent_path = fileInfo->path().parent(); - auto parent_str = parent_path ? parent_path.displayName(): nullptr; - - ui->fileName->setText(fileInfo->displayName()); - if(parent_str) { - ui->location->setText(parent_str.get()); - } - else { - ui->location->clear(); - } - auto mtime = QDateTime::fromMSecsSinceEpoch(fileInfo->mtime() * 1000); - ui->lastModified->setText(mtime.toString(Qt::SystemLocaleShortDate)); - auto atime = QDateTime::fromMSecsSinceEpoch(fileInfo->atime() * 1000); - ui->lastAccessed->setText(atime.toString(Qt::SystemLocaleShortDate)); - } - else { - ui->fileName->setText(tr("Multiple Files")); - ui->fileName->setEnabled(false); - } - - initApplications(); // init applications combo box - - // calculate total file sizes - fileSizeTimer = new QTimer(this); - connect(fileSizeTimer, &QTimer::timeout, this, &FilePropsDialog::onFileSizeTimerTimeout); - fileSizeTimer->start(600); - - connect(totalSizeJob, &Fm::TotalSizeJob::finished, this, &FilePropsDialog::onDeepCountJobFinished, Qt::BlockingQueuedConnection); - totalSizeJob->setAutoDelete(true); - totalSizeJob->runAsync(); -} - -void FilePropsDialog::onDeepCountJobFinished() { - onFileSizeTimerTimeout(); // update file size display - - totalSizeJob = nullptr; - - // stop the timer - if(fileSizeTimer) { - fileSizeTimer->stop(); - delete fileSizeTimer; - fileSizeTimer = nullptr; - } -} - -void FilePropsDialog::onFileSizeTimerTimeout() { - if(totalSizeJob && !totalSizeJob->isCancelled()) { - // FIXME: - // OMG! It's really unbelievable that Qt developers only implement - // QObject::tr(... int n). GNU gettext developers are smarter and - // they use unsigned long instead of int. - // We cannot use Qt here to handle plural forms. So sad. :-( - QString str = Fm::formatFileSize(totalSizeJob->totalSize(), fm_config->si_unit) % - QString(" (%1 B)").arg(totalSizeJob->totalSize()); - // tr(" (%n) byte(s)", "", deepCountJob->total_size); - ui->fileSize->setText(str); - - str = Fm::formatFileSize(totalSizeJob->totalOnDiskSize(), fm_config->si_unit) % - QString(" (%1 B)").arg(totalSizeJob->totalOnDiskSize()); - // tr(" (%n) byte(s)", "", deepCountJob->total_ondisk_size); - ui->onDiskSize->setText(str); - } -} - -void FilePropsDialog::onIconButtonclicked() { - QString iconDir; - QString iconThemeName = QIcon::themeName(); - QStringList icons = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, - "icons", - QStandardPaths::LocateDirectory); - for (QStringList::ConstIterator it = icons.constBegin(); it != icons.constEnd(); ++it) { - QString iconThemeFolder = *it + '/' + iconThemeName; - if (QDir(iconThemeFolder).exists() && QFileInfo(iconThemeFolder).permission(QFileDevice::ReadUser)) { - // give priority to the "places" folder - const QString places = iconThemeFolder + QLatin1String("/places"); - if (QDir(places).exists() && QFileInfo(places).permission(QFileDevice::ReadUser)) { - iconDir = places; - } - else { - iconDir = iconThemeFolder; - } - break; - } - } - if(iconDir.isEmpty()) { - iconDir = QStandardPaths::locate(QStandardPaths::GenericDataLocation, - "icons", - QStandardPaths::LocateDirectory); - if(iconDir.isEmpty()) { - return; - } - } - const QString iconPath = QFileDialog::getOpenFileName(this, tr("Select an icon"), - iconDir, - tr("Images (*.png *.xpm *.svg *.svgz )")); - if(!iconPath.isEmpty()) { - QStringList parts = iconPath.split("/", QString::SkipEmptyParts); - if(!parts.isEmpty()) { - QString iconName = parts.at(parts.count() - 1); - int ln = iconName.lastIndexOf("."); - if(ln > -1) { - iconName.remove(ln, iconName.length() - ln); - customIcon = QIcon::fromTheme(iconName); - ui->iconButton->setIcon(customIcon); - } - } - } -} - -void FilePropsDialog::accept() { - - // applications - if(mimeType && ui->openWith->isChanged()) { - auto currentApp = ui->openWith->selectedApp(); - g_app_info_set_as_default_for_type(currentApp.get(), mimeType->name(), nullptr); - } - - // check if chown or chmod is needed - uid_t newUid = uidFromName(ui->owner->text()); - gid_t newGid = gidFromName(ui->ownerGroup->text()); - bool needChown = (newUid != INVALID_UID && newUid != uid) || (newGid != INVALID_GID && newGid != gid); - - int newOwnerPermSel = ui->ownerPerm->currentIndex(); - int newGroupPermSel = ui->groupPerm->currentIndex(); - int newOtherPermSel = ui->otherPerm->currentIndex(); - Qt::CheckState newExecCheckState = ui->executable->checkState(); - bool needChmod = ((newOwnerPermSel != ownerPermSel) || - (newGroupPermSel != groupPermSel) || - (newOtherPermSel != otherPermSel) || - (newExecCheckState != execCheckState)); - - if(needChmod || needChown) { - FileOperation* op = new FileOperation(FileOperation::ChangeAttr, fileInfos_.paths()); - if(needChown) { - // don't do chown if new uid/gid and the original ones are actually the same. - if(newUid == uid) { - newUid = INVALID_UID; - } - if(newGid == gid) { - newGid = INVALID_GID; - } - op->setChown(newUid, newGid); - } - if(needChmod) { - mode_t newMode = 0; - mode_t newModeMask = 0; - // FIXME: we need to make sure that folders with "r" permission also have "x" - // at the same time. Otherwise, it's not able to browse the folder later. - if(newOwnerPermSel != ownerPermSel && newOwnerPermSel != ACCESS_NO_CHANGE) { - // owner permission changed - newModeMask |= (S_IRUSR | S_IWUSR); // affect user bits - if(newOwnerPermSel == ACCESS_READ_ONLY) { - newMode |= S_IRUSR; - } - else if(newOwnerPermSel == ACCESS_READ_WRITE) { - newMode |= (S_IRUSR | S_IWUSR); - } - } - if(newGroupPermSel != groupPermSel && newGroupPermSel != ACCESS_NO_CHANGE) { - qDebug("newGroupPermSel: %d", newGroupPermSel); - // group permission changed - newModeMask |= (S_IRGRP | S_IWGRP); // affect group bits - if(newGroupPermSel == ACCESS_READ_ONLY) { - newMode |= S_IRGRP; - } - else if(newGroupPermSel == ACCESS_READ_WRITE) { - newMode |= (S_IRGRP | S_IWGRP); - } - } - if(newOtherPermSel != otherPermSel && newOtherPermSel != ACCESS_NO_CHANGE) { - // other permission changed - newModeMask |= (S_IROTH | S_IWOTH); // affect other bits - if(newOtherPermSel == ACCESS_READ_ONLY) { - newMode |= S_IROTH; - } - else if(newOtherPermSel == ACCESS_READ_WRITE) { - newMode |= (S_IROTH | S_IWOTH); - } - } - if(newExecCheckState != execCheckState && newExecCheckState != Qt::PartiallyChecked) { - // executable state changed - newModeMask |= (S_IXUSR | S_IXGRP | S_IXOTH); - if(newExecCheckState == Qt::Checked) { - newMode |= (S_IXUSR | S_IXGRP | S_IXOTH); - } - } - op->setChmod(newMode, newModeMask); - - if(hasDir) { // if there are some dirs in our selected files - QMessageBox::StandardButton r = QMessageBox::question(this, - tr("Apply changes"), - tr("Do you want to recursively apply these changes to all files and sub-folders?"), - QMessageBox::Yes | QMessageBox::No); - if(r == QMessageBox::Yes) { - op->setRecursiveChattr(true); - } - } - } - op->setAutoDestroy(true); - op->run(); - } - - // Renaming - if(singleFile) { - QString new_name = ui->fileName->text(); - if(fileInfo->displayName() != new_name) { - auto path = fileInfo->path(); - auto parent_path = path.parent(); - auto dest = parent_path.child(new_name.toLocal8Bit().constData()); - Fm::GErrorPtr err; - if(!g_file_move(path.gfile().get(), dest.gfile().get(), - GFileCopyFlags(G_FILE_COPY_ALL_METADATA | - G_FILE_COPY_NO_FALLBACK_FOR_MOVE | - G_FILE_COPY_NOFOLLOW_SYMLINKS), - nullptr, nullptr, nullptr, &err)) { - QMessageBox::critical(this, QObject::tr("Error"), err.message()); - } - } - } - - // Custom (folder) icon - if(!customIcon.isNull()) { - bool reloadNeeded(false); - QString iconNamne = customIcon.name(); - for(auto& fi: fileInfos_) { - std::shared_ptr icon = fi->icon(); - if (!fi->icon() || fi->icon()->qicon().name() != iconNamne) { - auto dot_dir = CStrPtr{g_build_filename(fi->path().localPath().get(), ".directory", nullptr)}; - GKeyFile* kf = g_key_file_new(); - g_key_file_set_string(kf, "Desktop Entry", "Icon", iconNamne.toLocal8Bit().constData()); - Fm::GErrorPtr err; - if (!g_key_file_save_to_file(kf, dot_dir.get(), &err)) { - QMessageBox::critical(this, QObject::tr("Custom Icon Error"), err.message()); - } - else { - reloadNeeded = true; - } - g_key_file_free(kf); - } - } - if(reloadNeeded) { - // since there can be only one parent dir, only one reload is needed - auto parent = fileInfo->path().parent(); - if(parent.isValid()) { - auto folder = Fm::Folder::fromPath(parent); - if(folder->isLoaded()) { - folder->reload(); - } - } - } - } - - QDialog::accept(); -} - -void FilePropsDialog::initOwner() { - if(allNative) { - if(uid != DIFFERENT_UIDS) { - ui->owner->setText(uidToName(uid)); - } - if(gid != DIFFERENT_GIDS) { - ui->ownerGroup->setText(gidToName(gid)); - } - - if(geteuid() != 0) { // on local filesystems, only root can do chown. - ui->owner->setEnabled(false); - ui->ownerGroup->setEnabled(false); - } - } -} - - -} // namespace Fm diff --git a/src/filepropsdialog.h b/src/filepropsdialog.h deleted file mode 100644 index 4762a67..0000000 --- a/src/filepropsdialog.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_FILEPROPSDIALOG_H -#define FM_FILEPROPSDIALOG_H - -#include "libfmqtglobals.h" -#include -#include -#include - -#include "core/fileinfo.h" -#include "core/totalsizejob.h" - -namespace Ui { -class FilePropsDialog; -} - -namespace Fm { - -class LIBFM_QT_API FilePropsDialog : public QDialog { - Q_OBJECT - -public: - explicit FilePropsDialog(Fm::FileInfoList files, QWidget* parent = 0, Qt::WindowFlags f = 0); - virtual ~FilePropsDialog(); - - virtual void accept(); - - static FilePropsDialog* showForFile(std::shared_ptr file, QWidget* parent = 0) { - Fm::FileInfoList files; - files.push_back(std::move(file)); - FilePropsDialog* dlg = showForFiles(files, parent); - return dlg; - } - - static FilePropsDialog* showForFiles(Fm::FileInfoList files, QWidget* parent = 0) { - FilePropsDialog* dlg = new FilePropsDialog(std::move(files), parent); - dlg->show(); - return dlg; - } - -private: - void initGeneralPage(); - void initApplications(); - void initPermissionsPage(); - void initOwner(); - -private Q_SLOTS: - void onDeepCountJobFinished(); - void onFileSizeTimerTimeout(); - void onIconButtonclicked(); - -private: - Ui::FilePropsDialog* ui; - Fm::FileInfoList fileInfos_; // list of all file infos - std::shared_ptr fileInfo; // file info of the first file in the list - bool singleType; // all files are of the same type? - bool singleFile; // only one file is selected? - bool hasDir; // is there any dir in the files? - bool allNative; // all files are on native UNIX filesystems (not virtual or remote) - QIcon customIcon; // custom (folder) icon (wiil be checked for its nullity) - - std::shared_ptr mimeType; // mime type of the files - - uid_t uid; // owner uid of the files, INVALID_UID means all files do not have the same uid - gid_t gid; // owner gid of the files, INVALID_GID means all files do not have the same uid - mode_t ownerPerm; // read permission of the files, -1 means not all files have the same value - int ownerPermSel; - mode_t groupPerm; // read permission of the files, -1 means not all files have the same value - int groupPermSel; - mode_t otherPerm; // read permission of the files, -1 means not all files have the same value - int otherPermSel; - mode_t execPerm; // exec permission of the files - Qt::CheckState execCheckState; - - Fm::TotalSizeJob* totalSizeJob; // job used to count total size - QTimer* fileSizeTimer; -}; - -} - -#endif // FM_FILEPROPSDIALOG_H diff --git a/src/filesearch.ui b/src/filesearch.ui deleted file mode 100644 index d8c0e1a..0000000 --- a/src/filesearch.ui +++ /dev/null @@ -1,574 +0,0 @@ - - - SearchDialog - - - - 0 - 0 - 512 - 420 - - - - Search Files - - - - - - - - - - - 0 - - - - Name/Location - - - - - - File Name Patterns: - - - - - - * - - - - - - - Case insensitive - - - - - - - Use regular expression - - - true - - - - - - - - - - Places to Search: - - - - - - - - - - - - - &Add - - - - - - - - - - - - &Remove - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - Search in sub directories - - - - - - - Search for hidden files - - - - - - - - - - - File Type - - - - - - Only search for files of following types: - - - - - - Text files - - - - - - - Image files - - - - - - - Audio files - - - - - - - Video files - - - - - - - Documents - - - - - - - Folders - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - Content - - - - - - File contains: - - - - - - - - - Case insensiti&ve - - - - - - - &Use regular expression - - - true - - - - - - - - - - Qt::Vertical - - - - 20 - 186 - - - - - - - - - Properties - - - - - - File Size: - - - - - - - - Smaller than: - - - - - - - false - - - - - - - false - - - - - - - Larger than: - - - - - - - false - - - 2 - - - - Bytes - - - - - KiB - - - - - MiB - - - - - GiB - - - - - - - - false - - - 2 - - - - Bytes - - - - - KiB - - - - - MiB - - - - - GiB - - - - - - - - - - - - - Qt::LeftToRight - - - Last Modified Time: - - - - - - - - Earlier than: - - - - - - - Later than: - - - - - - - false - - - true - - - - - - - false - - - true - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - SearchDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - SearchDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - largerThan - toggled(bool) - minSizeUnit - setEnabled(bool) - - - 93 - 84 - - - 403 - 88 - - - - - smallerThan - toggled(bool) - maxSize - setEnabled(bool) - - - 96 - 119 - - - 241 - 123 - - - - - largerThan - toggled(bool) - minSize - setEnabled(bool) - - - 93 - 84 - - - 241 - 88 - - - - - smallerThan - toggled(bool) - maxSizeUnit - setEnabled(bool) - - - 96 - 119 - - - 403 - 123 - - - - - laterThan - toggled(bool) - minTime - setEnabled(bool) - - - 88 - 223 - - - 319 - 226 - - - - - earlierThan - toggled(bool) - maxTime - setEnabled(bool) - - - 93 - 190 - - - 319 - 193 - - - - - diff --git a/src/filesearchdialog.cpp b/src/filesearchdialog.cpp deleted file mode 100644 index 2ee1af7..0000000 --- a/src/filesearchdialog.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2015 Hong Jen Yee (PCMan) - * - * 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 "filesearchdialog.h" -#include -#include "fm-search.h" -#include "ui_filesearch.h" -#include -#include -#include - -namespace Fm { - -FileSearchDialog::FileSearchDialog(QStringList paths, QWidget* parent, Qt::WindowFlags f): - QDialog(parent, f), - ui(new Ui::SearchDialog()) { - ui->setupUi(this); - ui->minSize->setMaximum(std::numeric_limits().max()); - ui->maxSize->setMaximum(std::numeric_limits().max()); - for(const QString& path : qAsConst(paths)) { - ui->listView->addItem(path); - } - - ui->maxTime->setDate(QDate::currentDate()); - ui->minTime->setDate(QDate::currentDate()); - - connect(ui->addPath, &QPushButton::clicked, this, &FileSearchDialog::onAddPath); - connect(ui->removePath, &QPushButton::clicked, this, &FileSearchDialog::onRemovePath); - - ui->namePatterns->setFocus(); -} - -FileSearchDialog::~FileSearchDialog() { - delete ui; -} - -void FileSearchDialog::accept() { - // build the search:/// uri - int n = ui->listView->count(); - if(n > 0) { - FmSearch* search = fm_search_new(); - for(int i = 0; i < n; ++i) { // add directories - QListWidgetItem* item = ui->listView->item(i); - fm_search_add_dir(search, item->text().toLocal8Bit().constData()); - } - - fm_search_set_recursive(search, ui->recursiveSearch->isChecked()); - fm_search_set_show_hidden(search, ui->searchHidden->isChecked()); - fm_search_set_name_patterns(search, ui->namePatterns->text().toUtf8().constData()); - fm_search_set_name_ci(search, ui->nameCaseInsensitive->isChecked()); - fm_search_set_name_regex(search, ui->nameRegExp->isChecked()); - - fm_search_set_content_pattern(search, ui->contentPattern->text().toUtf8().constData()); - fm_search_set_content_ci(search, ui->contentCaseInsensitive->isChecked()); - fm_search_set_content_regex(search, ui->contentRegExp->isChecked()); - - // search for the files of specific mime-types - if(ui->searchTextFiles->isChecked()) { - fm_search_add_mime_type(search, "text/plain"); - } - if(ui->searchImages->isChecked()) { - fm_search_add_mime_type(search, "image/*"); - } - if(ui->searchAudio->isChecked()) { - fm_search_add_mime_type(search, "audio/*"); - } - if(ui->searchVideo->isChecked()) { - fm_search_add_mime_type(search, "video/*"); - } - if(ui->searchFolders->isChecked()) { - fm_search_add_mime_type(search, "inode/directory"); - } - if(ui->searchDocuments->isChecked()) { - const char* doc_types[] = { - "application/pdf", - /* "text/html;" */ - "application/vnd.oasis.opendocument.*", - "application/vnd.openxmlformats-officedocument.*", - "application/msword;application/vnd.ms-word", - "application/msexcel;application/vnd.ms-excel" - }; - for(unsigned int i = 0; i < sizeof(doc_types) / sizeof(char*); ++i) { - fm_search_add_mime_type(search, doc_types[i]); - } - } - - // search based on file size - const unsigned int unit_bytes[] = {1, (1024), (1024 * 1024), (1024 * 1024 * 1024)}; - if(ui->largerThan->isChecked()) { - guint64 size = ui->minSize->value() * unit_bytes[ui->minSizeUnit->currentIndex()]; - fm_search_set_min_size(search, size); - } - - if(ui->smallerThan->isChecked()) { - guint64 size = ui->maxSize->value() * unit_bytes[ui->maxSizeUnit->currentIndex()]; - fm_search_set_max_size(search, size); - } - - // search based on file mtime (we only support date in YYYY-MM-DD format) - if(ui->earlierThan->isChecked()) { - fm_search_set_max_mtime(search, ui->maxTime->date().toString(QStringLiteral("yyyy-MM-dd")).toUtf8().constData()); - } - if(ui->laterThan->isChecked()) { - fm_search_set_min_mtime(search, ui->minTime->date().toString(QStringLiteral("yyyy-MM-dd")).toUtf8().constData()); - } - - searchUri_ = FilePath{fm_search_to_gfile(search), false}; - - fm_search_free(search); - } - else { - QMessageBox::critical(this, tr("Error"), tr("You should add at least one directory to search.")); - return; - } - QDialog::accept(); -} - -void FileSearchDialog::onAddPath() { - QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder")); - if(dir.isEmpty()) { - return; - } - // avoid adding duplicated items - if(ui->listView->findItems(dir, Qt::MatchFixedString | Qt::MatchCaseSensitive).isEmpty()) { - ui->listView->addItem(dir); - } -} - -void FileSearchDialog::onRemovePath() { - // remove selected items - const auto itemList = ui->listView->selectedItems(); - for(QListWidgetItem* const item : itemList) { - delete item; - } -} - -void FileSearchDialog::setNameCaseInsensitive(bool caseInsensitive) { - ui->nameCaseInsensitive->setChecked(caseInsensitive); -} - -void FileSearchDialog::setContentCaseInsensitive(bool caseInsensitive) { - ui->contentCaseInsensitive->setChecked(caseInsensitive); -} - -void FileSearchDialog::setNameRegexp(bool reg) { - ui->nameRegExp->setChecked(reg); -} - -void FileSearchDialog::setContentRegexp(bool reg) { - ui->contentRegExp->setChecked(reg); -} - -void FileSearchDialog::setRecursive(bool rec) { - ui->recursiveSearch->setChecked(rec); -} - -void FileSearchDialog::setSearchhHidden(bool hidden) { - ui->searchHidden->setChecked(hidden); -} - -bool FileSearchDialog::nameCaseInsensitive() const { - return ui->nameCaseInsensitive->isChecked(); -} - -bool FileSearchDialog::contentCaseInsensitive() const { - return ui->contentCaseInsensitive->isChecked(); -} - -bool FileSearchDialog::nameRegexp() const { - return ui->nameRegExp->isChecked(); -} - -bool FileSearchDialog::contentRegexp() const { - return ui->contentRegExp->isChecked(); -} - -bool FileSearchDialog::recursive() const { - return ui->recursiveSearch->isChecked(); -} - -bool FileSearchDialog::searchhHidden() const { - return ui->searchHidden->isChecked(); -} - -} diff --git a/src/filesearchdialog.h b/src/filesearchdialog.h deleted file mode 100644 index 5f6126b..0000000 --- a/src/filesearchdialog.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2015 Hong Jen Yee (PCMan) - * - * 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_FILESEARCHDIALOG_H -#define FM_FILESEARCHDIALOG_H - -#include "libfmqtglobals.h" -#include -#include "core/filepath.h" - -namespace Ui { -class SearchDialog; -} - -namespace Fm { - -class LIBFM_QT_API FileSearchDialog : public QDialog { -public: - explicit FileSearchDialog(QStringList paths = QStringList(), QWidget* parent = 0, Qt::WindowFlags f = 0); - ~FileSearchDialog(); - - const FilePath& searchUri() const { - return searchUri_; - } - - virtual void accept(); - - bool nameCaseInsensitive() const; - void setNameCaseInsensitive(bool caseInsensitive); - - bool contentCaseInsensitive() const; - void setContentCaseInsensitive(bool caseInsensitive); - - bool nameRegexp() const; - void setNameRegexp(bool reg); - - bool contentRegexp() const; - void setContentRegexp(bool reg); - - bool recursive() const; - void setRecursive(bool rec); - - bool searchhHidden() const; - void setSearchhHidden(bool hidden); - -private Q_SLOTS: - void onAddPath(); - void onRemovePath(); - -private: - Ui::SearchDialog* ui; - FilePath searchUri_; -}; - -} - -#endif // FM_FILESEARCHDIALOG_H diff --git a/src/fm-search.c b/src/fm-search.c deleted file mode 100644 index 4fa20d7..0000000 --- a/src/fm-search.c +++ /dev/null @@ -1,317 +0,0 @@ -/* - * fm-search-uri.c - * - * Copyright 2015 Hong Jen Yee (PCMan) - * Copyright 2012-2014 Andriy Grytsenko (LStranger) - * - * 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 "fm-search.h" -#include - -struct _FmSearch -{ - gboolean recursive; - gboolean show_hidden; - char* name_patterns; - gboolean name_ci; - gboolean name_regex; - char* content_pattern; - gboolean content_ci; - gboolean content_regex; - GList* mime_types; - GList* search_path_list; - guint64 max_size; - guint64 min_size; - char* max_mtime; - char* min_mtime; -}; - -FmSearch* fm_search_new (void) -{ - FmSearch* search = (FmSearch*)g_slice_new0(FmSearch); - return search; -} - -void fm_search_free(FmSearch* search) -{ - g_list_free_full(search->mime_types, (GDestroyNotify)g_free); - g_list_free_full(search->search_path_list, (GDestroyNotify)g_free); - g_free(search->name_patterns); - g_free(search->content_pattern); - g_free(search->max_mtime); - g_free(search->min_mtime); - g_slice_free(FmSearch, search); -} - -gboolean fm_search_get_recursive(FmSearch* search) -{ - return search->recursive; -} - -void fm_search_set_recursive(FmSearch* search, gboolean recursive) -{ - search->recursive = recursive; -} - -gboolean fm_search_get_show_hidden(FmSearch* search) -{ - return search->show_hidden; -} - -void fm_search_set_show_hidden(FmSearch* search, gboolean show_hidden) -{ - search->show_hidden = show_hidden; -} - -const char* fm_search_get_name_patterns(FmSearch* search) -{ - return search->name_patterns; -} - -void fm_search_set_name_patterns(FmSearch* search, const char* name_patterns) -{ - g_free(search->name_patterns); - search->name_patterns = g_strdup(name_patterns); -} - -gboolean fm_search_get_name_ci(FmSearch* search) -{ - return search->name_ci; -} - -void fm_search_set_name_ci(FmSearch* search, gboolean name_ci) -{ - search->name_ci = name_ci; -} - -gboolean fm_search_get_name_regex(FmSearch* search) -{ - return search->name_regex; -} - -void fm_search_set_name_regex(FmSearch* search, gboolean name_regex) -{ - search->name_regex = name_regex; -} - -const char* fm_search_get_content_pattern(FmSearch* search) -{ - return search->content_pattern; -} - -void fm_search_set_content_pattern(FmSearch* search, const char* content_pattern) -{ - g_free(search->content_pattern); - search->content_pattern = g_strdup(content_pattern); -} - -gboolean fm_search_get_content_ci(FmSearch* search) -{ - return search->content_ci; -} - -void fm_search_set_content_ci(FmSearch* search, gboolean content_ci) -{ - search->content_ci = content_ci; -} - -gboolean fm_search_get_content_regex(FmSearch* search) -{ - return search->content_regex; -} - -void fm_search_set_content_regex(FmSearch* search, gboolean content_regex) -{ - search->content_regex = content_regex; -} - -void fm_search_add_dir(FmSearch* search, const char* dir) -{ - GList* l = g_list_find_custom(search->search_path_list, dir, (GCompareFunc)strcmp); - if(!l) - search->search_path_list = g_list_prepend(search->search_path_list, g_strdup(dir)); -} - -void fm_search_remove_dir(FmSearch* search, const char* dir) -{ - GList* l = g_list_find_custom(search->search_path_list, dir, (GCompareFunc)strcmp); - if(G_LIKELY(l)) - { - g_free(l->data); - search->search_path_list = g_list_delete_link(search->search_path_list, l); - } -} - -GList* fm_search_get_dirs(FmSearch* search) -{ - return search->search_path_list; -} - -void fm_search_add_mime_type(FmSearch* search, const char* mime_type) -{ - GList* l = g_list_find_custom(search->mime_types, mime_type, (GCompareFunc)strcmp); - if(!l) - search->mime_types = g_list_prepend(search->mime_types, g_strdup(mime_type)); -} - -void fm_search_remove_mime_type(FmSearch* search, const char* mime_type) -{ - GList* l = g_list_find_custom(search->mime_types, mime_type, (GCompareFunc)strcmp); - if(G_LIKELY(l)) - { - g_free(l->data); - search->mime_types = g_list_delete_link(search->mime_types, l); - } -} - -GList* fm_search_get_mime_types(FmSearch* search) -{ - return search->mime_types; -} - -guint64 fm_search_get_max_size(FmSearch* search) -{ - return search->max_size; -} - -void fm_search_set_max_size(FmSearch* search, guint64 size) -{ - search->max_size = size; -} - -guint64 fm_search_get_min_size(FmSearch* search) -{ - return search->min_size; -} - -void fm_search_set_min_size(FmSearch* search, guint64 size) -{ - search->min_size = size; -} - -/* format of mtime: YYYY-MM-DD */ -const char* fm_search_get_max_mtime(FmSearch* search) -{ - return search->max_mtime; -} - -void fm_search_set_max_mtime(FmSearch* search, const char* mtime) -{ - g_free(search->max_mtime); - search->max_mtime = g_strdup(mtime); -} - -/* format of mtime: YYYY-MM-DD */ -const char* fm_search_get_min_mtime(FmSearch* search) -{ - return search->min_mtime; -} - -void fm_search_set_min_mtime(FmSearch* search, const char* mtime) -{ - g_free(search->min_mtime); - search->min_mtime = g_strdup(mtime); -} - -/* really build the path */ -GFile* fm_search_to_gfile(FmSearch* search) -{ - GFile* search_path = NULL; - GString* search_str = g_string_sized_new(1024); - /* build the search:// URI to perform the search */ - g_string_append(search_str, "search://"); - - if(search->search_path_list) /* we need to have at least one dir path */ - { - char *escaped; - /* add paths */ - GList* l; - for(l = search->search_path_list; ; ) - { - char *path_str = (char*)l->data; - /* escape possible '?' and ',' */ - escaped = g_uri_escape_string(path_str, "!$&'()*+:;=/@", TRUE); - g_string_append(search_str, escaped); - g_free(escaped); - - l = l->next; - if(!l) /* no more items */ - break; - g_string_append_c(search_str, ','); /* separator for paths */ - } - - g_string_append_c(search_str, '?'); - g_string_append_printf(search_str, "recursive=%c", search->recursive ? '1' : '0'); - g_string_append_printf(search_str, "&show_hidden=%c", search->show_hidden ? '1' : '0'); - if(search->name_patterns && *search->name_patterns) - { - /* escape ampersands in pattern */ - escaped = g_uri_escape_string(search->name_patterns, ":/?#[]@!$'()*+,;", TRUE); - if(search->name_regex) - g_string_append_printf(search_str, "&name_regex=%s", escaped); - else - g_string_append_printf(search_str, "&name=%s", escaped); - if(search->name_ci) - g_string_append_printf(search_str, "&name_ci=%c", search->name_ci ? '1' : '0'); - g_free(escaped); - } - - if(search->content_pattern && *search->content_pattern) - { - /* escape ampersands in pattern */ - escaped = g_uri_escape_string(search->content_pattern, ":/?#[]@!$'()*+,;^<>{}", TRUE); - if(search->content_regex) - g_string_append_printf(search_str, "&content_regex=%s", escaped); - else - g_string_append_printf(search_str, "&content=%s", escaped); - g_free(escaped); - if(search->content_ci) - g_string_append_printf(search_str, "&content_ci=%c", search->content_ci ? '1' : '0'); - } - - /* search for the files of specific mime-types */ - if(search->mime_types) - { - GList* l; - g_string_append(search_str, "&mime_types="); - for(l = search->mime_types; l; l=l->next) - { - const char* mime_type = (const char*)l->data; - g_string_append(search_str, mime_type); - if(l->next) - g_string_append_c(search_str, ';'); - } - } - - if(search->min_size) - g_string_append_printf(search_str, "&min_size=%llu", (unsigned long long)search->min_size); - - if(search->max_size) - g_string_append_printf(search_str, "&max_size=%llu", (unsigned long long)search->max_size); - - if(search->min_mtime) - g_string_append_printf(search_str, "&min_mtime=%s", search->min_mtime); - - if(search->max_mtime) - g_string_append_printf(search_str, "&max_mtime=%s", search->max_mtime); - - search_path = g_file_new_for_uri(search_str->str); - g_string_free(search_str, TRUE); - } - return search_path; -} diff --git a/src/fm-search.h b/src/fm-search.h deleted file mode 100644 index f85b152..0000000 --- a/src/fm-search.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * fm-search-uri.h - * - * Copyright 2015 Hong Jen Yee (PCMan) - * Copyright 2012-2014 Andriy Grytsenko (LStranger) - * - * 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 - * - */ - -/* FmSearch implements a tool used to generate a search:// URI used by libfm to search for files. - * This API might become part of libfm in the future. - */ - -#ifndef _FM_SEARCH_H_ -#define _FM_SEARCH_H_ - -#include - -G_BEGIN_DECLS - -typedef struct _FmSearch FmSearch; - -FmSearch* fm_search_new(void); -void fm_search_free(FmSearch* search); - -GFile* fm_search_to_gfile(FmSearch* search); - -gboolean fm_search_get_recursive(FmSearch* search); -void fm_search_set_recursive(FmSearch* search, gboolean recursive); - -gboolean fm_search_get_show_hidden(FmSearch* search); -void fm_search_set_show_hidden(FmSearch* search, gboolean show_hidden); - -const char* fm_search_get_name_patterns(FmSearch* search); -void fm_search_set_name_patterns(FmSearch* search, const char* name_patterns); - -gboolean fm_search_get_name_ci(FmSearch* search); -void fm_search_set_name_ci(FmSearch* search, gboolean name_ci); - -gboolean fm_search_get_name_regex(FmSearch* search); -void fm_search_set_name_regex(FmSearch* search, gboolean name_regex); - -const char* fm_search_get_content_pattern(FmSearch* search); -void fm_search_set_content_pattern(FmSearch* search, const char* content_pattern); - -gboolean fm_search_get_content_ci(FmSearch* search); -void fm_search_set_content_ci(FmSearch* search, gboolean content_ci); - -gboolean fm_search_get_content_regex(FmSearch* search); -void fm_search_set_content_regex(FmSearch* search, gboolean content_regex); - -void fm_search_add_dir(FmSearch* search, const char* dir); -void fm_search_remove_dir(FmSearch* search, const char* dir); -GList* fm_search_get_dirs(FmSearch* search); - -void fm_search_add_mime_type(FmSearch* search, const char* mime_type); -void fm_search_remove_mime_type(FmSearch* search, const char* mime_type); -GList* fm_search_get_mime_types(FmSearch* search); - -guint64 fm_search_get_max_size(FmSearch* search); -void fm_search_set_max_size(FmSearch* search, guint64 size); - -guint64 fm_search_get_min_size(FmSearch* search); -void fm_search_set_min_size(FmSearch* search, guint64 size); - -/* format of mtime: YYYY-MM-DD */ -const char* fm_search_get_max_mtime(FmSearch* search); -void fm_search_set_max_mtime(FmSearch* search, const char* mtime); - -/* format of mtime: YYYY-MM-DD */ -const char* fm_search_get_min_mtime(FmSearch* search); -void fm_search_set_min_mtime(FmSearch* search, const char* mtime); - -G_END_DECLS - -#endif /* _FM_SEARCH_H_ */ diff --git a/src/folderconfig.h b/src/folderconfig.h deleted file mode 100644 index 3a0843c..0000000 --- a/src/folderconfig.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2016 Hong Jen Yee (PCMan) - * - * 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_FOLDER_CONFIG_H__ -#define __LIBFM_QT_FM_FOLDER_CONFIG_H__ - -#include -#include -#include -#include "libfmqtglobals.h" - -#include "core/filepath.h" - -namespace Fm { - -// FIXME: port to the new API and drop libfm dependency - -class LIBFM_QT_API FolderConfig { -public: - - FolderConfig(const Fm::FilePath& path) { - FmPath* fmpath = fm_path_new_for_gfile(path.gfile().get()); - dataPtr_ = reinterpret_cast(fm_folder_config_open(fmpath)); - fm_path_unref(fmpath); - } - - - // default constructor - FolderConfig() { - dataPtr_ = nullptr; - } - - - // move constructor - FolderConfig(FolderConfig&& other) noexcept { - dataPtr_ = reinterpret_cast(other.takeDataPtr()); - } - - - // destructor - ~FolderConfig() { - if(dataPtr_ != nullptr) { - fm_folder_config_close(dataPtr_, nullptr); - } - } - - - // create a wrapper for the data pointer without increasing the reference count - static FolderConfig wrapPtr(FmFolderConfig* dataPtr) { - FolderConfig obj; - obj.dataPtr_ = reinterpret_cast(dataPtr); - return obj; - } - - // disown the managed data pointer - FmFolderConfig* takeDataPtr() { - FmFolderConfig* data = reinterpret_cast(dataPtr_); - dataPtr_ = nullptr; - return data; - } - - // get the raw pointer wrapped - FmFolderConfig* dataPtr() { - return reinterpret_cast(dataPtr_); - } - - // automatic type casting - operator FmFolderConfig* () { - return dataPtr(); - } - - // automatic type casting - operator void* () { - return dataPtr(); - } - - - - // move assignment - FolderConfig& operator=(FolderConfig&& other) noexcept { - dataPtr_ = reinterpret_cast(other.takeDataPtr()); - return *this; - } - - bool isNull() { - return (dataPtr_ == nullptr); - } - - // methods - - static void saveCache(void) { - fm_folder_config_save_cache(); - } - - - void purge(void) { - fm_folder_config_purge(dataPtr()); - } - - - void removeKey(const char* key) { - fm_folder_config_remove_key(dataPtr(), key); - } - - - void setStringList(const char* key, const gchar* const list[], gsize length) { - fm_folder_config_set_string_list(dataPtr(), key, list, length); - } - - - void setString(const char* key, const char* string) { - fm_folder_config_set_string(dataPtr(), key, string); - } - - - void setBoolean(const char* key, gboolean val) { - fm_folder_config_set_boolean(dataPtr(), key, val); - } - - - void setDouble(const char* key, gdouble val) { - fm_folder_config_set_double(dataPtr(), key, val); - } - - - void setUint64(const char* key, guint64 val) { - fm_folder_config_set_uint64(dataPtr(), key, val); - } - - - void setInteger(const char* key, gint val) { - fm_folder_config_set_integer(dataPtr(), key, val); - } - - - char** getStringList(const char* key, gsize* length) { - return fm_folder_config_get_string_list(dataPtr(), key, length); - } - - - char* getString(const char* key) { - return fm_folder_config_get_string(dataPtr(), key); - } - - - bool getBoolean(const char* key, gboolean* val) { - return fm_folder_config_get_boolean(dataPtr(), key, val); - } - - - bool getDouble(const char* key, gdouble* val) { - return fm_folder_config_get_double(dataPtr(), key, val); - } - - - bool getUint64(const char* key, guint64* val) { - return fm_folder_config_get_uint64(dataPtr(), key, val); - } - - - bool getInteger(const char* key, gint* val) { - return fm_folder_config_get_integer(dataPtr(), key, val); - } - - - bool isEmpty(void) { - return fm_folder_config_is_empty(dataPtr()); - } - - -// the wrapped object cannot be copied. -private: - FolderConfig(const FolderConfig& other) = delete; - FolderConfig& operator=(const FolderConfig& other) = delete; - - -private: - FmFolderConfig* dataPtr_; // data pointer for the underlying C struct - -}; - - -} - -#endif // __LIBFM_QT_FM_FOLDER_CONFIG_H__ diff --git a/src/folderitemdelegate.cpp b/src/folderitemdelegate.cpp deleted file mode 100644 index 3cb0b6a..0000000 --- a/src/folderitemdelegate.cpp +++ /dev/null @@ -1,443 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 "folderitemdelegate.h" -#include "foldermodel.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Fm { - -FolderItemDelegate::FolderItemDelegate(QAbstractItemView* view, QObject* parent): - QStyledItemDelegate(parent ? parent : view), - symlinkIcon_(QIcon::fromTheme("emblem-symbolic-link")), - fileInfoRole_(Fm::FolderModel::FileInfoRole), - iconInfoRole_(-1), - margins_(QSize(3, 3)), - hasEditor_(false) { - connect(this, &QAbstractItemDelegate::closeEditor, [=]{hasEditor_ = false;}); -} - -FolderItemDelegate::~FolderItemDelegate() { - -} - -QSize FolderItemDelegate::iconViewTextSize(const QModelIndex& index) const { - QStyleOptionViewItem opt; - initStyleOption(&opt, index); - opt.decorationSize = iconSize_.isValid() ? iconSize_ : QSize(0, 0); - opt.decorationAlignment = Qt::AlignHCenter | Qt::AlignTop; - opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; - QRectF textRect(0, 0, - itemSize_.width() - 2 * margins_.width(), - itemSize_.height() - 2 * margins_.height() - opt.decorationSize.height()); - drawText(nullptr, opt, textRect); // passing nullptr for painter will calculate the bounding rect only - return textRect.toRect().size(); -} - -QSize FolderItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { - QVariant value = index.data(Qt::SizeHintRole); - if(value.isValid()) { - // no further processing if the size is specified by the data model - return qvariant_cast(value); - } - - if(option.decorationPosition == QStyleOptionViewItem::Top || - option.decorationPosition == QStyleOptionViewItem::Bottom) { - // we handle vertical layout just by returning our item size - return itemSize_; - } - - // The default size hint of the horizontal layout isn't reliable - // because Qt calculates the row size based on the real icon size, - // which may not be equal to the requested icon size on various occasions. - // So, we do as in QStyledItemDelegate::sizeHint() but use the requested size. - QStyleOptionViewItem opt = option; - initStyleOption(&opt, index); - opt.decorationSize = option.decorationSize; // requested by the view - const QWidget* widget = option.widget; - QStyle* style = widget ? widget->style() : QApplication::style(); - return style->sizeFromContents(QStyle::CT_ItemViewItem, &opt, QSize(), widget); -} - -QIcon::Mode FolderItemDelegate::iconModeFromState(const QStyle::State state) { - - if(state & QStyle::State_Enabled) { - return (state & QStyle::State_Selected) ? QIcon::Selected : QIcon::Normal; - } - - return QIcon::Disabled; -} - -void FolderItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { - if(!index.isValid()) - return; - - // get emblems for this icon - std::forward_list> icon_emblems; - auto fmicon = index.data(iconInfoRole_).value>(); - if(fmicon) { - icon_emblems = fmicon->emblems(); - } - // get file info for the item - auto file = index.data(fileInfoRole_).value>(); - const auto& emblems = file ? file->emblems() : icon_emblems; - - QStyleOptionViewItem opt = option; - initStyleOption(&opt, index); - - // distinguish the hidden items visually by making their texts italic - if(file && file->isHidden()) { - QFont f(opt.font); - f.setItalic(true); - opt.font = f; - } - - bool isSymlink = file && file->isSymlink(); - // vertical layout (icon mode, thumbnail mode) - if(option.decorationPosition == QStyleOptionViewItem::Top || - option.decorationPosition == QStyleOptionViewItem::Bottom) { - painter->save(); - painter->setClipRect(option.rect); - - opt.decorationAlignment = Qt::AlignHCenter | Qt::AlignTop; - opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; - - // draw the icon - QIcon::Mode iconMode = iconModeFromState(opt.state); - QPoint iconPos(opt.rect.x() + (opt.rect.width() - option.decorationSize.width()) / 2, opt.rect.y() + margins_.height()); - QPixmap pixmap = opt.icon.pixmap(option.decorationSize, iconMode); - // in case the pixmap is smaller than the requested size - QSize margin = ((option.decorationSize - pixmap.size()) / 2).expandedTo(QSize(0, 0)); - bool isCut = index.data(FolderModel::FileIsCutRole).toBool(); - if(isCut) { - painter->save(); - painter->setOpacity(0.45); - } - painter->drawPixmap(iconPos + QPoint(margin.width(), margin.height()), pixmap); - if(isCut) { - painter->restore(); - } - - // draw some emblems for the item if needed - if(isSymlink) { - // draw the emblem for symlinks - painter->drawPixmap(iconPos, symlinkIcon_.pixmap(option.decorationSize / 2, iconMode)); - } - - // draw other emblems if there's any - if(!emblems.empty()) { - // FIXME: we only support one emblem now - QPoint emblemPos(opt.rect.x() + opt.rect.width() / 2, opt.rect.y() + option.decorationSize.height() / 2); - QIcon emblem = emblems.front()->qicon(); - painter->drawPixmap(emblemPos, emblem.pixmap(option.decorationSize / 2, iconMode)); - } - - // draw the text - QSize drawAreaSize = itemSize_ - 2 * margins_; - // The text rect dimensions should be exactly as they were in sizeHint() - QRectF textRect(opt.rect.x() + (opt.rect.width() - drawAreaSize.width()) / 2, - opt.rect.y() + margins_.height() + option.decorationSize.height(), - drawAreaSize.width(), - drawAreaSize.height() - option.decorationSize.height()); - drawText(painter, opt, textRect); - painter->restore(); - } - else { // horizontal layout (list view) - - // let QStyledItemDelegate does its default painting - // FIXME: For better text alignment, here we should increase - // the icon width if it's smaller that the requested size - QStyledItemDelegate::paint(painter, opt, index); - - // draw emblems if needed - if(isSymlink || !emblems.empty()) { - QIcon::Mode iconMode = iconModeFromState(opt.state); - // draw some emblems for the item if needed - if(isSymlink) { - QPoint iconPos(opt.rect.x(), opt.rect.y() + (opt.rect.height() - option.decorationSize.height()) / 2); - painter->drawPixmap(iconPos, symlinkIcon_.pixmap(option.decorationSize / 2, iconMode)); - } - else { - // FIXME: we only support one emblem now - QPoint iconPos(opt.rect.x() + option.decorationSize.width() / 2, opt.rect.y() + opt.rect.height() / 2); - QIcon emblem = emblems.front()->qicon(); - painter->drawPixmap(iconPos, emblem.pixmap(option.decorationSize / 2, iconMode)); - } - } - } -} - -// if painter is nullptr, the method calculate the bounding rectangle of the text and save it to textRect -void FolderItemDelegate::drawText(QPainter* painter, QStyleOptionViewItem& opt, QRectF& textRect) const { - QTextLayout layout(opt.text, opt.font); - QTextOption textOption; - textOption.setAlignment(opt.displayAlignment); - textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - // FIXME: textOption.setTextDirection(opt.direction); ? - if(opt.text.isRightToLeft()) { - textOption.setTextDirection(Qt::RightToLeft); - } - else { - textOption.setTextDirection(Qt::LeftToRight); - } - layout.setTextOption(textOption); - qreal height = 0; - qreal width = 0; - int visibleLines = 0; - layout.beginLayout(); - QString elidedText; - textRect.adjust(2, 2, -2, -2); // a 2-px margin is considered at FolderView::updateGridSize() - for(;;) { - QTextLine line = layout.createLine(); - if(!line.isValid()) { - break; - } - line.setLineWidth(textRect.width()); - height += opt.fontMetrics.leading(); - line.setPosition(QPointF(0, height)); - if((height + line.height() + textRect.y()) > textRect.bottom()) { - // if part of this line falls outside the textRect, ignore it and quit. - QTextLine lastLine = layout.lineAt(visibleLines - 1); - elidedText = opt.text.mid(lastLine.textStart()); - elidedText = opt.fontMetrics.elidedText(elidedText, opt.textElideMode, textRect.width()); - if(visibleLines == 1) { // this is the only visible line - width = textRect.width(); - } - break; - } - height += line.height(); - width = qMax(width, line.naturalTextWidth()); - ++ visibleLines; - } - layout.endLayout(); - width = qMax(width, (qreal)opt.fontMetrics.width(elidedText)); - - // draw background for selected item - QRectF boundRect = layout.boundingRect(); - //qDebug() << "bound rect: " << boundRect << "width: " << width; - boundRect.setWidth(width); - boundRect.setHeight(height); - boundRect.moveTo(textRect.x() + (textRect.width() - width) / 2, textRect.y()); - - QRectF selRect = boundRect.adjusted(-2, -2, 2, 2); - - if(!painter) { // no painter, calculate the bounding rect only - textRect = selRect; - return; - } - - // Respect the active and inactive palettes (some styles can use different colors for them). - // Also, take into account a probable disabled palette. - QPalette::ColorGroup cg = (opt.state & QStyle::State_Enabled) - ? (opt.state & QStyle::State_Active) - ? QPalette::Active - : QPalette::Inactive - : QPalette::Disabled; - if(opt.state & QStyle::State_Selected) { - if(!opt.widget) { - painter->fillRect(selRect, opt.palette.highlight()); - } - painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); - } - else { - painter->setPen(opt.palette.color(cg, QPalette::Text)); - } - - if(opt.state & QStyle::State_Selected || opt.state & QStyle::State_MouseOver) { - if(const QWidget* widget = opt.widget) { // let the style engine do it - QStyle* style = widget->style() ? widget->style() : qApp->style(); - QStyleOptionViewItem o(opt); - o.text = QString(); - o.rect = selRect.toAlignedRect().intersected(opt.rect); // due to clipping and rounding, we might lose 1px - o.showDecorationSelected = true; - style->drawPrimitive(QStyle::PE_PanelItemViewItem, &o, painter, widget); - } - } - - // draw shadow for text if the item is not selected and a shadow color is set - if(!(opt.state & QStyle::State_Selected) && shadowColor_.isValid()) { - QPen prevPen = painter->pen(); - painter->setPen(QPen(shadowColor_)); - for(int i = 0; i < visibleLines; ++i) { - QTextLine line = layout.lineAt(i); - if(i == (visibleLines - 1) && !elidedText.isEmpty()) { // the last line, draw elided text - QPointF pos(boundRect.x() + line.position().x() + 1, boundRect.y() + line.y() + line.ascent() + 1); - painter->drawText(pos, elidedText); - } - else { - line.draw(painter, textRect.topLeft() + QPointF(1, 1)); - } - } - painter->setPen(prevPen); - } - - // draw text - for(int i = 0; i < visibleLines; ++i) { - QTextLine line = layout.lineAt(i); - if(i == (visibleLines - 1) && !elidedText.isEmpty()) { // the last line, draw elided text - QPointF pos(boundRect.x() + line.position().x(), boundRect.y() + line.y() + line.ascent()); - painter->drawText(pos, elidedText); - } - else { - line.draw(painter, textRect.topLeft()); - } - } - - if(opt.state & QStyle::State_HasFocus) { - // draw focus rect - QStyleOptionFocusRect o; - o.QStyleOption::operator=(opt); - o.rect = selRect.toRect(); // subElementRect(SE_ItemViewItemFocusRect, vopt, widget); - o.state |= QStyle::State_KeyboardFocusChange; - o.state |= QStyle::State_Item; - QPalette::ColorGroup cg = (opt.state & QStyle::State_Enabled) - ? QPalette::Normal : QPalette::Disabled; - o.backgroundColor = opt.palette.color(cg, (opt.state & QStyle::State_Selected) - ? QPalette::Highlight : QPalette::Window); - if(const QWidget* widget = opt.widget) { - QStyle* style = widget->style() ? widget->style() : qApp->style(); - style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, widget); - } - } -} - -/* - * The following methods are for inline renaming. - */ - -QWidget* FolderItemDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { - hasEditor_ = true; - if (option.decorationPosition == QStyleOptionViewItem::Top - || option.decorationPosition == QStyleOptionViewItem::Bottom) - { - // in icon view, we use QTextEdit as the editor (and not QPlainTextEdit - // because the latter always shows an empty space at the bottom) - QTextEdit *textEdit = new QTextEdit(parent); - textEdit->setAcceptRichText(false); - - // Since the text color on desktop is inherited from desktop foreground color, - // it may not be suitable. So, we reset it by using the app palette. - QPalette p = textEdit->palette(); - p.setColor(QPalette::Text, qApp->palette().text().color()); - textEdit->setPalette(p); - - textEdit->ensureCursorVisible(); - textEdit->setFocusPolicy(Qt::StrongFocus); - textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - textEdit->setContentsMargins(0, 0, 0, 0); - return textEdit; - } - else { - // return the default line-edit in other views and - // ensure that its background isn't transparent (on the side-pane) - QWidget* editor = QStyledItemDelegate::createEditor(parent, option, index); - QPalette p = editor->palette(); - p.setColor(QPalette::Base, QApplication::palette().color(QPalette::Base)); - editor->setPalette(p); - return editor; - } -} - -void FolderItemDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { - if (!index.isValid()) { - return; - } - const QString currentName = index.data(Qt::EditRole).toString(); - - if (QTextEdit* textEdit = qobject_cast(editor)) { - textEdit->setPlainText(currentName); - textEdit->setUndoRedoEnabled(false); - textEdit->setAlignment(Qt::AlignCenter); - textEdit->setUndoRedoEnabled(true); - // select text appropriately - QTextCursor cur = textEdit->textCursor(); - int end; - if (index.data(Fm::FolderModel::FileIsDirRole).toBool() || !currentName.contains(".")) { - end = currentName.size(); - } - else { - end = currentName.lastIndexOf("."); - } - cur.setPosition(end, QTextCursor::KeepAnchor); - textEdit->setTextCursor(cur); - } - else if (QLineEdit* lineEdit = qobject_cast(editor)) { - lineEdit->setText(currentName); - if (!index.data(Fm::FolderModel::FileIsDirRole).toBool() && currentName.contains(".")) - { - /* Qt will call QLineEdit::selectAll() after calling setEditorData() in - qabstractitemview.cpp -> QAbstractItemViewPrivate::editor(). Therefore, - we cannot select a part of the text in the usual way here. */ - QTimer::singleShot(0, [lineEdit]() { - int length = lineEdit->text().lastIndexOf("."); - lineEdit->setSelection(0, length); - }); - } - } -} - -bool FolderItemDelegate::eventFilter(QObject* object, QEvent* event) { - QWidget *editor = qobject_cast(object); - if (editor && event->type() == QEvent::KeyPress) { - int k = static_cast(event)->key(); - if (k == Qt::Key_Return || k == Qt::Key_Enter) { - Q_EMIT QAbstractItemDelegate::commitData(editor); - Q_EMIT QAbstractItemDelegate::closeEditor(editor, QAbstractItemDelegate::NoHint); - return true; - } - } - return QStyledItemDelegate::eventFilter(object, event); -} - -void FolderItemDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const { - if (option.decorationPosition == QStyleOptionViewItem::Top - || option.decorationPosition == QStyleOptionViewItem::Bottom) { - // give all of the available space to the editor - QStyleOptionViewItem opt = option; - initStyleOption(&opt, index); - opt.decorationAlignment = Qt::AlignHCenter|Qt::AlignTop; - opt.displayAlignment = Qt::AlignTop|Qt::AlignHCenter; - QRect textRect(opt.rect.x(), - opt.rect.y() + margins_.height() + option.decorationSize.height(), - itemSize_.width(), - itemSize_.height() - margins_.height() - option.decorationSize.height()); - int frame = editor->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &option, editor); - editor->setGeometry(textRect.adjusted(-frame, -frame, frame, frame)); - } - else { - // use the default editor geometry in compact view - QStyledItemDelegate::updateEditorGeometry(editor, option, index); - } -} - - -} // namespace Fm diff --git a/src/folderitemdelegate.h b/src/folderitemdelegate.h deleted file mode 100644 index 1886e39..0000000 --- a/src/folderitemdelegate.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_FOLDERITEMDELEGATE_H -#define FM_FOLDERITEMDELEGATE_H - -#include "libfmqtglobals.h" -#include -class QAbstractItemView; - -namespace Fm { - -class LIBFM_QT_API FolderItemDelegate : public QStyledItemDelegate { - Q_OBJECT -public: - explicit FolderItemDelegate(QAbstractItemView* view, QObject* parent = nullptr); - - virtual ~FolderItemDelegate(); - - inline void setItemSize(QSize size) { - itemSize_ = size; - } - - inline QSize itemSize() const { - return itemSize_; - } - - inline void setIconSize(QSize size) { - iconSize_ = size; - } - - inline QSize iconSize() const { - return iconSize_; - } - - int fileInfoRole() { - return fileInfoRole_; - } - - void setFileInfoRole(int role) { - fileInfoRole_ = role; - } - - int iconInfoRole() { - return iconInfoRole_; - } - - void setIconInfoRole(int role) { - iconInfoRole_ = role; - } - - // only support vertical layout (icon view mode: text below icon) - void setShadowColor(const QColor& shadowColor) { - shadowColor_ = shadowColor; - } - - // only support vertical layout (icon view mode: text below icon) - const QColor& shadowColor() const { - return shadowColor_; - } - - // only support vertical layout (icon view mode: text below icon) - void setMargins(QSize margins) { - margins_ = margins.expandedTo(QSize(0, 0)); - } - - QSize getMargins() const { - return margins_; - } - - bool hasEditor() const { - return hasEditor_; - } - - virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; - - virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; - - virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; - - virtual void setEditorData(QWidget* editor, const QModelIndex& index) const; - - virtual bool eventFilter(QObject* object, QEvent* event); - - virtual void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const; - - QSize iconViewTextSize(const QModelIndex& index) const; - -private: - void drawText(QPainter* painter, QStyleOptionViewItem& opt, QRectF& textRect) const; - - static QIcon::Mode iconModeFromState(QStyle::State state); - -private: - QIcon symlinkIcon_; - QSize iconSize_; - QSize itemSize_; - int fileInfoRole_; - int iconInfoRole_; - QColor shadowColor_; - QSize margins_; - mutable bool hasEditor_; -}; - -} - -#endif // FM_FOLDERITEMDELEGATE_H diff --git a/src/foldermenu.cpp b/src/foldermenu.cpp deleted file mode 100644 index 1a87dd0..0000000 --- a/src/foldermenu.cpp +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * Copyright (C) 2012 - 2014 Andriy Grytsenko (LStranger) - * - * 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 "foldermenu.h" -#include "createnewmenu.h" -#include "filepropsdialog.h" -#include "folderview.h" -#include "utilities.h" -#include // for memset -#include -#include "customaction_p.h" -#include "customactions/fileaction.h" -#include - -namespace Fm { - -FolderMenu::FolderMenu(FolderView* view, QWidget* parent): - QMenu(parent), - view_(view) { - - ProxyFolderModel* model = view_->model(); - - createAction_ = new QAction(tr("Create &New"), this); - addAction(createAction_); - - createAction_->setMenu(new CreateNewMenu(view_, view_->path(), this)); - - separator1_ = addSeparator(); - - pasteAction_ = new QAction(QIcon::fromTheme("edit-paste"), tr("&Paste"), this); - addAction(pasteAction_); - connect(pasteAction_, &QAction::triggered, this, &FolderMenu::onPasteActionTriggered); - - separator2_ = addSeparator(); - - selectAllAction_ = new QAction(tr("Select &All"), this); - addAction(selectAllAction_); - connect(selectAllAction_, &QAction::triggered, this, &FolderMenu::onSelectAllActionTriggered); - - invertSelectionAction_ = new QAction(tr("Invert Selection"), this); - addAction(invertSelectionAction_); - connect(invertSelectionAction_, &QAction::triggered, this, &FolderMenu::onInvertSelectionActionTriggered); - - separator3_ = addSeparator(); - - sortAction_ = new QAction(tr("Sorting"), this); - addAction(sortAction_); - createSortMenu(); - sortAction_->setMenu(sortMenu_); - - showHiddenAction_ = new QAction(tr("Show Hidden"), this); - addAction(showHiddenAction_); - showHiddenAction_->setCheckable(true); - showHiddenAction_->setChecked(model->showHidden()); - connect(showHiddenAction_, &QAction::triggered, this, &FolderMenu::onShowHiddenActionTriggered); - - auto folderInfo = view_->folderInfo(); - if(folderInfo) { // should never be null (see FolderView::onFileClicked) - // DES-EMA custom actions integration - FileInfoList files; - files.push_back(folderInfo); - auto custom_actions = FileActionItem::get_actions_for_files(files); - for(auto& item: custom_actions) { - if(item && !(item->get_target() & FILE_ACTION_TARGET_CONTEXT)) { - continue; // this item is not for context menu - } - if(item == custom_actions.front() && item && !item->is_action()) { - addSeparator(); // before all custom actions - } - addCustomActionItem(this, item); - } - - // disable paste acton if it can't be used - pasteAction_->setEnabled(folderInfo->isWritable()); - } - - separator4_ = addSeparator(); - - propertiesAction_ = new QAction(tr("Folder Pr&operties"), this); - addAction(propertiesAction_); - connect(propertiesAction_, &QAction::triggered, this, &FolderMenu::onPropertiesActionTriggered); -} - -FolderMenu::~FolderMenu() { -} - -void FolderMenu::addCustomActionItem(QMenu* menu, std::shared_ptr item) { - if(!item) { - return; - } - if(item->is_action() && !(item->get_target() & FILE_ACTION_TARGET_CONTEXT)) { - return; - } - - CustomAction* action = new CustomAction(item, menu); - menu->addAction(action); - if(item->is_menu()) { - auto& subitems = item->get_sub_items(); - if(!subitems.empty()) { - QMenu* submenu = new QMenu(menu); - for(auto& subitem: subitems) { - addCustomActionItem(submenu, subitem); - } - action->setMenu(submenu); - } - } - else if(item->is_action()) { - connect(action, &QAction::triggered, this, &FolderMenu::onCustomActionTrigerred); - } -} - -void FolderMenu::onCustomActionTrigerred() { - CustomAction* action = static_cast(sender()); - auto& item = action->item(); - auto folderInfo = view_->folderInfo(); - if(folderInfo) { - CStrPtr output; - FileInfoList file_list; - file_list.push_back(folderInfo); - item->launch(nullptr, file_list, output); - if(output) { - QMessageBox::information(this, tr("Output"), output.get()); - } - } -} - -void FolderMenu::addSortMenuItem(QString title, int id) { - QAction* action = new QAction(title, this); - sortMenu_->addAction(action); - action->setCheckable(true); - sortActionGroup_->addAction(action); - connect(action, &QAction::triggered, this, &FolderMenu::onSortActionTriggered); - sortActions_[id] = action; -} - -void FolderMenu::createSortMenu() { - ProxyFolderModel* model = view_->model(); - - sortMenu_ = new QMenu(this); - sortActionGroup_ = new QActionGroup(sortMenu_); - sortActionGroup_->setExclusive(true); - - std::memset(sortActions_, 0, sizeof(sortActions_)); - - addSortMenuItem(tr("By File Name"), FolderModel::ColumnFileName); - addSortMenuItem(tr("By Modification Time"), FolderModel::ColumnFileMTime); - addSortMenuItem(tr("By File Size"), FolderModel::ColumnFileSize); - addSortMenuItem(tr("By File Type"), FolderModel::ColumnFileType); - addSortMenuItem(tr("By File Owner"), FolderModel::ColumnFileOwner); - - int col = model->sortColumn(); - - if(col >= 0 && col < FolderModel::NumOfColumns) { - sortActions_[col]->setChecked(true);; - } - - sortMenu_->addSeparator(); - - QActionGroup* group = new QActionGroup(this); - group->setExclusive(true); - actionAscending_ = new QAction(tr("Ascending"), this); - actionAscending_->setCheckable(true); - sortMenu_->addAction(actionAscending_); - group->addAction(actionAscending_); - - actionDescending_ = new QAction(tr("Descending"), this); - actionDescending_->setCheckable(true); - sortMenu_->addAction(actionDescending_); - group->addAction(actionDescending_); - - if(model->sortOrder() == Qt::AscendingOrder) { - actionAscending_->setChecked(true); - } - else { - actionDescending_->setChecked(true); - } - - connect(actionAscending_, &QAction::triggered, this, &FolderMenu::onSortOrderActionTriggered); - connect(actionDescending_, &QAction::triggered, this, &FolderMenu::onSortOrderActionTriggered); - - sortMenu_->addSeparator(); - - QAction* actionFolderFirst = new QAction(tr("Folder First"), this); - sortMenu_->addAction(actionFolderFirst); - actionFolderFirst->setCheckable(true); - - if(model->folderFirst()) { - actionFolderFirst->setChecked(true); - } - - connect(actionFolderFirst, &QAction::triggered, this, &FolderMenu::onFolderFirstActionTriggered); - - QAction* actionCaseSensitive = new QAction(tr("Case Sensitive"), this); - sortMenu_->addAction(actionCaseSensitive); - actionCaseSensitive->setCheckable(true); - - if(model->sortCaseSensitivity() == Qt::CaseSensitive) { - actionCaseSensitive->setChecked(true); - } - - connect(actionCaseSensitive, &QAction::triggered, this, &FolderMenu::onCaseSensitiveActionTriggered); -} - -void FolderMenu::onPasteActionTriggered() { - auto folderPath = view_->path(); - if(folderPath) { - pasteFilesFromClipboard(folderPath); - } -} - -void FolderMenu::onSelectAllActionTriggered() { - view_->selectAll(); -} - -void FolderMenu::onInvertSelectionActionTriggered() { - view_->invertSelection(); -} - -void FolderMenu::onSortActionTriggered(bool /*checked*/) { - ProxyFolderModel* model = view_->model(); - - if(model) { - QAction* action = static_cast(sender()); - - for(int col = 0; col < FolderModel::NumOfColumns; ++col) { - if(action == sortActions_[col]) { - model->sort(col, model->sortOrder()); - break; - } - } - } -} - -void FolderMenu::onSortOrderActionTriggered(bool /*checked*/) { - ProxyFolderModel* model = view_->model(); - - if(model) { - QAction* action = static_cast(sender()); - Qt::SortOrder order; - - if(action == actionAscending_) { - order = Qt::AscendingOrder; - } - else { - order = Qt::DescendingOrder; - } - - model->sort(model->sortColumn(), order); - } -} - -void FolderMenu::onShowHiddenActionTriggered(bool checked) { - ProxyFolderModel* model = view_->model(); - - if(model) { - qDebug("show hidden: %d", checked); - model->setShowHidden(checked); - } -} - -void FolderMenu::onCaseSensitiveActionTriggered(bool checked) { - ProxyFolderModel* model = view_->model(); - - if(model) { - model->setSortCaseSensitivity(checked ? Qt::CaseSensitive : Qt::CaseInsensitive); - } -} - -void FolderMenu::onFolderFirstActionTriggered(bool checked) { - ProxyFolderModel* model = view_->model(); - - if(model) { - model->setFolderFirst(checked); - } -} - -void FolderMenu::onPropertiesActionTriggered() { - auto folderInfo = view_->folderInfo(); - if(folderInfo) { - FilePropsDialog::showForFile(folderInfo); - } -} - -} // namespace Fm diff --git a/src/foldermenu.h b/src/foldermenu.h deleted file mode 100644 index 70d1e25..0000000 --- a/src/foldermenu.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_FOLDERMENU_H -#define FM_FOLDERMENU_H - -#include "libfmqtglobals.h" -#include -#include -#include "foldermodel.h" - -class QAction; - -namespace Fm { - -class FolderView; -class FileActionItem; - -class LIBFM_QT_API FolderMenu : public QMenu { - Q_OBJECT - -public: - explicit FolderMenu(FolderView* view, QWidget* parent = 0); - virtual ~FolderMenu(); - - QAction* createAction() { - return createAction_; - } - - QAction* separator1() { - return separator1_; - } - - QAction* pasteAction() { - return pasteAction_; - } - - QAction* separator2() { - return separator2_; - } - - QAction* selectAllAction() { - return selectAllAction_; - } - - QAction* invertSelectionAction() { - return invertSelectionAction_; - } - - QAction* separator3() { - return separator3_; - } - - QAction* sortAction() { - return sortAction_; - } - - QAction* showHiddenAction() { - return showHiddenAction_; - } - - QAction* separator4() { - return separator4_; - } - - QAction* propertiesAction() { - return propertiesAction_; - } - - FolderView* view() { - return view_; - } - -protected: - void addCustomActionItem(QMenu* menu, std::shared_ptr item); - -protected Q_SLOTS: - void onPasteActionTriggered(); - void onSelectAllActionTriggered(); - void onInvertSelectionActionTriggered(); - void onSortActionTriggered(bool checked); - void onSortOrderActionTriggered(bool checked); - void onShowHiddenActionTriggered(bool checked); - void onCaseSensitiveActionTriggered(bool checked); - void onFolderFirstActionTriggered(bool checked); - void onPropertiesActionTriggered(); - void onCustomActionTrigerred(); - -private: - void createSortMenu(); - void addSortMenuItem(QString title, int id); - -private: - FolderView* view_; - QAction* createAction_; - QAction* separator1_; - QAction* pasteAction_; - QAction* separator2_; - QAction* selectAllAction_; - QAction* invertSelectionAction_; - QAction* separator3_; - QAction* sortAction_; - QActionGroup* sortActionGroup_; - QMenu* sortMenu_; - QAction* sortActions_[FolderModel::NumOfColumns]; - QAction* actionAscending_; - QAction* actionDescending_; - QAction* showHiddenAction_; - QAction* separator4_; - QAction* propertiesAction_; -}; - -} - -#endif // FM_FOLDERMENU_H diff --git a/src/foldermodel.cpp b/src/foldermodel.cpp deleted file mode 100644 index 972e17f..0000000 --- a/src/foldermodel.cpp +++ /dev/null @@ -1,557 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 "foldermodel.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "utilities.h" -#include "fileoperation.h" - -namespace Fm { - -FolderModel::FolderModel(): - hasPendingThumbnailHandler_{false} { -} - -FolderModel::~FolderModel() { - // if the thumbnail requests list is not empty, cancel them - for(auto job: pendingThumbnailJobs_) { - job->cancel(); - } -} - -void FolderModel::setFolder(const std::shared_ptr& new_folder) { - if(folder_) { - removeAll(); // remove old items - } - if(new_folder) { - folder_ = new_folder; - connect(folder_.get(), &Fm::Folder::startLoading, this, &FolderModel::onStartLoading); - connect(folder_.get(), &Fm::Folder::finishLoading, this, &FolderModel::onFinishLoading); - connect(folder_.get(), &Fm::Folder::filesAdded, this, &FolderModel::onFilesAdded); - connect(folder_.get(), &Fm::Folder::filesChanged, this, &FolderModel::onFilesChanged); - connect(folder_.get(), &Fm::Folder::filesRemoved, this, &FolderModel::onFilesRemoved); - // handle the case if the folder is already loaded - if(folder_->isLoaded()) { - insertFiles(0, folder_->files()); - } - } -} - -void FolderModel::onStartLoading() { - // remove all items - removeAll(); -} - -void FolderModel::onFinishLoading() { -} - -void FolderModel::onFilesAdded(const Fm::FileInfoList& files) { - int n_files = files.size(); - beginInsertRows(QModelIndex(), items.count(), items.count() + n_files - 1); - for(auto& info : files) { - FolderModelItem item(info); - /* - if(fm_file_info_is_hidden(info)) { - model->hiddenItems.append(item); - continue; - } - */ - items.append(item); - } - endInsertRows(); - Q_EMIT filesAdded(files); -} - -void FolderModel::onFilesChanged(std::vector& files) { - for(auto& change : files) { - int row; - auto& oldInfo = change.first; - auto& newInfo = change.second; - QList::iterator it = findItemByFileInfo(oldInfo.get(), &row); - if(it != items.end()) { - FolderModelItem& item = *it; - // try to update the item - item.info = newInfo; - item.thumbnails.clear(); - QModelIndex index = createIndex(row, 0, &item); - Q_EMIT dataChanged(index, index); - if(oldInfo->size() != newInfo->size()) { - Q_EMIT fileSizeChanged(index); - } - } - } -} - -void FolderModel::onFilesRemoved(const Fm::FileInfoList& files) { - for(auto& info : files) { - int row; - QList::iterator it = findItemByName(info->name().c_str(), &row); - if(it != items.end()) { - beginRemoveRows(QModelIndex(), row, row); - items.erase(it); - endRemoveRows(); - } - } -} - -void FolderModel::loadPendingThumbnails() { - hasPendingThumbnailHandler_ = false; - for(auto& item: thumbnailData_) { - if(!item.pendingThumbnails_.empty()) { - auto job = new Fm::ThumbnailJob(std::move(item.pendingThumbnails_), item.size_); - pendingThumbnailJobs_.push_back(job); - job->setAutoDelete(true); - connect(job, &Fm::ThumbnailJob::thumbnailLoaded, this, &FolderModel::onThumbnailLoaded, Qt::BlockingQueuedConnection); - connect(job, &Fm::ThumbnailJob::finished, this, &FolderModel::onThumbnailJobFinished, Qt::BlockingQueuedConnection); - Fm::ThumbnailJob::threadPool()->start(job); - } - } -} - -void FolderModel::queueLoadThumbnail(const std::shared_ptr& file, int size) { - auto it = std::find_if(thumbnailData_.begin(), thumbnailData_.end(), [size](ThumbnailData& item){return item.size_ == size;}); - if(it != thumbnailData_.end()) { - it->pendingThumbnails_.push_back(file); - if(!hasPendingThumbnailHandler_) { - QTimer::singleShot(0, this, &FolderModel::loadPendingThumbnails); - hasPendingThumbnailHandler_ = true; - } - } -} - -void FolderModel::insertFiles(int row, const Fm::FileInfoList& files) { - int n_files = files.size(); - beginInsertRows(QModelIndex(), row, row + n_files - 1); - for(auto& info : files) { - FolderModelItem item(info); - items.append(item); - } - endInsertRows(); -} - -void FolderModel::setCutFiles(const QItemSelection& selection) { - if(folder_) { - if(!selection.isEmpty()) { - auto cutFilesHashSet = std::make_shared(); - folder_->setCutFiles(cutFilesHashSet); - const auto indexes = selection.indexes(); - for(const auto& index : indexes) { - auto item = itemFromIndex(index); - item->bindCutFiles(cutFilesHashSet); - cutFilesHashSet->insert(item->info->path().hash()); - } - } - Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0)); - } -} - -void FolderModel::removeAll() { - if(items.empty()) { - return; - } - beginRemoveRows(QModelIndex(), 0, items.size() - 1); - items.clear(); - endRemoveRows(); -} - -int FolderModel::rowCount(const QModelIndex& parent) const { - if(parent.isValid()) { - return 0; - } - return items.size(); -} - -int FolderModel::columnCount(const QModelIndex& parent = QModelIndex()) const { - if(parent.isValid()) { - return 0; - } - return NumOfColumns; -} - -FolderModelItem* FolderModel::itemFromIndex(const QModelIndex& index) const { - return reinterpret_cast(index.internalPointer()); -} - -std::shared_ptr FolderModel::fileInfoFromIndex(const QModelIndex& index) const { - FolderModelItem* item = itemFromIndex(index); - return item ? item->info : nullptr; -} - -QVariant FolderModel::data(const QModelIndex& index, int role/* = Qt::DisplayRole*/) const { - if(!index.isValid() || index.row() > items.size() || index.column() >= NumOfColumns) { - return QVariant(); - } - FolderModelItem* item = itemFromIndex(index); - auto info = item->info; - - bool isCut = false; - if(folder_ && Q_UNLIKELY(folder_->hasCutFiles())) { - isCut = item->isCut(); - } - - switch(role) { - case Qt::ToolTipRole: - return QVariant(item->displayName()); - case Qt::DisplayRole: { - switch(index.column()) { - case ColumnFileName: - return item->displayName(); - case ColumnFileType: - return QString(info->mimeType()->desc()); - case ColumnFileMTime: - return item->displayMtime(); - case ColumnFileSize: - return item->displaySize(); - case ColumnFileOwner: - return item->ownerName(); - case ColumnFileGroup: - return item->ownerGroup(); - } - break; - } - case Qt::DecorationRole: { - if(index.column() == 0) { - return QVariant(item->icon(isCut)); - } - break; - } - case Qt::EditRole: { - if(index.column() == 0) { - return QString::fromStdString(info->name()); - } - break; - } - case FileInfoRole: - return QVariant::fromValue(info); - case FileIsDirRole: - return QVariant(info->isDir()); - case FileIsCutRole: - return isCut; - } - return QVariant(); -} - -QVariant FolderModel::headerData(int section, Qt::Orientation orientation, int role/* = Qt::DisplayRole*/) const { - if(role == Qt::DisplayRole) { - if(orientation == Qt::Horizontal) { - QString title; - switch(section) { - case ColumnFileName: - title = tr("Name"); - break; - case ColumnFileType: - title = tr("Type"); - break; - case ColumnFileSize: - title = tr("Size"); - break; - case ColumnFileMTime: - title = tr("Modified"); - break; - case ColumnFileOwner: - title = tr("Owner"); - break; - case ColumnFileGroup: - title = tr("Group"); - break; - } - return QVariant(title); - } - } - return QVariant(); -} - -QModelIndex FolderModel::index(int row, int column, const QModelIndex& /*parent*/) const { - if(row < 0 || row >= items.size() || column < 0 || column >= NumOfColumns) { - return QModelIndex(); - } - const FolderModelItem& item = items.at(row); - return createIndex(row, column, (void*)&item); -} - -QModelIndex FolderModel::parent(const QModelIndex& /*index*/) const { - return QModelIndex(); -} - -Qt::ItemFlags FolderModel::flags(const QModelIndex& index) const { - // FIXME: should not return same flags unconditionally for all columns - Qt::ItemFlags flags; - if(index.isValid()) { - flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; - if(index.column() == ColumnFileName) { - flags |= (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled - | Qt::ItemIsEditable); // inline renaming); - } - } - else { - flags = Qt::ItemIsDropEnabled; - } - return flags; -} - -// FIXME: this is very inefficient and should be replaced with a -// more reasonable implementation later. -QList::iterator FolderModel::findItemByPath(const Fm::FilePath& path, int* row) { - QList::iterator it = items.begin(); - int i = 0; - while(it != items.end()) { - FolderModelItem& item = *it; - auto item_path = item.info->path(); - if(item_path == path) { - *row = i; - return it; - } - ++it; - ++i; - } - return items.end(); -} - -// FIXME: this is very inefficient and should be replaced with a -// more reasonable implementation later. -QList::iterator FolderModel::findItemByName(const char* name, int* row) { - QList::iterator it = items.begin(); - int i = 0; - while(it != items.end()) { - FolderModelItem& item = *it; - if(item.info->name() == name) { - *row = i; - return it; - } - ++it; - ++i; - } - return items.end(); -} - -QList< FolderModelItem >::iterator FolderModel::findItemByFileInfo(const Fm::FileInfo* info, int* row) { - QList::iterator it = items.begin(); - int i = 0; - while(it != items.end()) { - FolderModelItem& item = *it; - if(item.info.get() == info) { - *row = i; - return it; - } - ++it; - ++i; - } - return items.end(); -} - -QStringList FolderModel::mimeTypes() const { - //qDebug("FolderModel::mimeTypes"); - QStringList types = QAbstractItemModel::mimeTypes(); - // now types contains "application/x-qabstractitemmodeldatalist" - - // add support for freedesktop Xdnd direct save (XDS) protocol. - // https://www.freedesktop.org/wiki/Specifications/XDS/#index4h2 - // the real implementation is in FolderView::childDropEvent(). - types << "XdndDirectSave0"; - types << "text/uri-list"; - // types << "x-special/gnome-copied-files"; - return types; -} - -QMimeData* FolderModel::mimeData(const QModelIndexList& indexes) const { - QMimeData* data = QAbstractItemModel::mimeData(indexes); - //qDebug("FolderModel::mimeData"); - // build a uri list - QByteArray urilist; - urilist.reserve(4096); - - for(const auto& index : indexes) { - FolderModelItem* item = itemFromIndex(index); - if(item && item->info) { - auto path = item->info->path(); - if(path.isValid()) { - auto uri = path.uri(); - urilist.append(uri.get()); - urilist.append('\n'); - } - } - } - data->setData("text/uri-list", urilist); - - return data; -} - -bool FolderModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) { - //qDebug("FolderModel::dropMimeData"); - if(!folder_ || !data) { - return false; - } - Fm::FilePath destPath; - if(parent.isValid()) { // drop on an item - std::shared_ptr info; - if(row == -1 && column == -1) { - info = fileInfoFromIndex(parent); - } - else { - QModelIndex itemIndex = parent.child(row, column); - info = fileInfoFromIndex(itemIndex); - } - if(info) { - if (info->isDir()) { - destPath = info->path(); - } - else { - destPath = path(); // don't drop on file - } - } - else { - return false; - } - } - else { // drop on blank area of the folder - destPath = path(); - } - - // FIXME: should we put this in dropEvent handler of FolderView instead? - if(data->hasUrls()) { - //qDebug("drop action: %d", action); - auto srcPaths = pathListFromQUrls(data->urls()); - switch(action) { - case Qt::CopyAction: - FileOperation::copyFiles(srcPaths, destPath); - break; - case Qt::MoveAction: - FileOperation::moveFiles(srcPaths, destPath); - break; - case Qt::LinkAction: - FileOperation::symlinkFiles(srcPaths, destPath); - /* Falls through. */ - default: - return false; - } - return true; - } - else if(data->hasFormat("application/x-qabstractitemmodeldatalist")) { - return true; - } - return QAbstractListModel::dropMimeData(data, action, row, column, parent); -} - -Qt::DropActions FolderModel::supportedDropActions() const { - //qDebug("FolderModel::supportedDropActions"); - return Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; -} - -// ask the model to load thumbnails of the specified size -void FolderModel::cacheThumbnails(const int size) { - auto it = std::find_if(thumbnailData_.begin(), thumbnailData_.end(), [size](ThumbnailData& item){return item.size_ == size;}); - if(it != thumbnailData_.cend()) { - ++it->refCount_; - } - else { - thumbnailData_.push_front(ThumbnailData(size)); - } -} - -// ask the model to free cached thumbnails of the specified size -void FolderModel::releaseThumbnails(int size) { - auto prev = thumbnailData_.before_begin(); - for(auto it = thumbnailData_.begin(); it != thumbnailData_.end(); ++it) { - if(it->size_ == size) { - --it->refCount_; - if(it->refCount_ == 0) { - thumbnailData_.erase_after(prev); - } - - // remove all cached thumbnails of the specified size - QList::iterator itemIt; - for(itemIt = items.begin(); itemIt != items.end(); ++itemIt) { - FolderModelItem& item = *itemIt; - item.removeThumbnail(size); - } - break; - } - prev = it; - } -} - -void FolderModel::onThumbnailJobFinished() { - Fm::ThumbnailJob* job = static_cast(sender()); - auto it = std::find(pendingThumbnailJobs_.cbegin(), pendingThumbnailJobs_.cend(), job); - if(it != pendingThumbnailJobs_.end()) { - pendingThumbnailJobs_.erase(it); - } -} - -void FolderModel::onThumbnailLoaded(const std::shared_ptr& file, int size, const QImage& image) { - // find the model item this thumbnail belongs to - int row; - QList::iterator it = findItemByFileInfo(file.get(), &row); - if(it != items.end()) { - // the file is found in our model - FolderModelItem& item = *it; - QModelIndex index = createIndex(row, 0, (void*)&item); - // store the image in the folder model item. - FolderModelItem::Thumbnail* thumbnail = item.findThumbnail(size, false); - thumbnail->image = image; - thumbnail->transparent = false; - // qDebug("thumbnail loaded for: %s, size: %d", item.displayName.toUtf8().constData(), size); - if(image.isNull()) { - thumbnail->status = FolderModelItem::ThumbnailFailed; - } - else { - thumbnail->status = FolderModelItem::ThumbnailLoaded; - thumbnail->image = image; - - // tell the world that we have the thumbnail loaded - Q_EMIT thumbnailLoaded(index, size); - } - } -} - -// get a thumbnail of size at the index -// if a thumbnail is not yet loaded, this will initiate loading of the thumbnail. -QImage FolderModel::thumbnailFromIndex(const QModelIndex& index, int size) { - FolderModelItem* item = itemFromIndex(index); - if(item) { - FolderModelItem::Thumbnail* thumbnail = item->findThumbnail(size, item->isCut()); - // qDebug("FolderModel::thumbnailFromIndex: %d, %s", thumbnail->status, item->displayName.toUtf8().data()); - switch(thumbnail->status) { - case FolderModelItem::ThumbnailNotChecked: { - // load the thumbnail - queueLoadThumbnail(item->info, size); - thumbnail->status = FolderModelItem::ThumbnailLoading; - break; - } - case FolderModelItem::ThumbnailLoaded: - return thumbnail->image; - default: - ; - } - } - return QImage(); -} - - -} // namespace Fm diff --git a/src/foldermodel.h b/src/foldermodel.h deleted file mode 100644 index 8a680cb..0000000 --- a/src/foldermodel.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_FOLDERMODEL_H -#define FM_FOLDERMODEL_H - -#include "libfmqtglobals.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "foldermodelitem.h" - -#include "core/folder.h" -#include "core/thumbnailjob.h" - -namespace Fm { - -class LIBFM_QT_API FolderModel : public QAbstractListModel { - Q_OBJECT -public: - - enum Role { - FileInfoRole = Qt::UserRole, - FileIsDirRole, - FileIsCutRole - }; - - enum ColumnId { - ColumnFileName, - ColumnFileType, - ColumnFileSize, - ColumnFileMTime, - ColumnFileOwner, - ColumnFileGroup, - NumOfColumns - }; - -public: - explicit FolderModel(); - virtual ~FolderModel(); - - const std::shared_ptr& folder() const { - return folder_; - } - - void setFolder(const std::shared_ptr& new_folder); - - Fm::FilePath path() { - return folder_ ? folder_->path() : Fm::FilePath(); - } - - int rowCount(const QModelIndex& parent = QModelIndex()) const; - int columnCount(const QModelIndex& parent) const; - QVariant data(const QModelIndex& index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const; - QModelIndex parent(const QModelIndex& index) const; - // void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); - - Qt::ItemFlags flags(const QModelIndex& index) const; - - virtual QStringList mimeTypes() const; - virtual QMimeData* mimeData(const QModelIndexList& indexes) const; - virtual Qt::DropActions supportedDropActions() const; - virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); - - std::shared_ptr fileInfoFromIndex(const QModelIndex& index) const; - FolderModelItem* itemFromIndex(const QModelIndex& index) const; - QImage thumbnailFromIndex(const QModelIndex& index, int size); - - void cacheThumbnails(int size); - void releaseThumbnails(int size); - - void setCutFiles(const QItemSelection& selection); - -Q_SIGNALS: - void thumbnailLoaded(const QModelIndex& index, int size); - void fileSizeChanged(const QModelIndex& index); - void filesAdded(FileInfoList infoList); - -protected Q_SLOTS: - - void onStartLoading(); - void onFinishLoading(); - void onFilesAdded(const Fm::FileInfoList& files); - void onFilesChanged(std::vector& files); - void onFilesRemoved(const Fm::FileInfoList& files); - - void onThumbnailLoaded(const std::shared_ptr& file, int size, const QImage& image); - void onThumbnailJobFinished(); - void loadPendingThumbnails(); - -protected: - void queueLoadThumbnail(const std::shared_ptr& file, int size); - void insertFiles(int row, const Fm::FileInfoList& files); - void removeAll(); - QList::iterator findItemByPath(const Fm::FilePath& path, int* row); - QList::iterator findItemByName(const char* name, int* row); - QList::iterator findItemByFileInfo(const Fm::FileInfo* info, int* row); - -private: - - struct ThumbnailData { - ThumbnailData(int size): - size_{size}, - refCount_{1} { - } - - int size_; - int refCount_; - Fm::FileInfoList pendingThumbnails_; - }; - - std::shared_ptr folder_; - QList items; - - bool hasPendingThumbnailHandler_; - std::vector pendingThumbnailJobs_; - std::forward_list thumbnailData_; -}; - -} - -#endif // FM_FOLDERMODEL_H diff --git a/src/foldermodelitem.cpp b/src/foldermodelitem.cpp deleted file mode 100644 index 44922ce..0000000 --- a/src/foldermodelitem.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 "foldermodelitem.h" -#include -#include -#include "utilities.h" -#include "core/userinfocache.h" - -namespace Fm { - -FolderModelItem::FolderModelItem(const std::shared_ptr& _info): - info{_info} { - thumbnails.reserve(2); -} - -FolderModelItem::FolderModelItem(const FolderModelItem& other): - info{other.info}, - thumbnails{other.thumbnails} { -} - -FolderModelItem::~FolderModelItem() { -} - -QString FolderModelItem::ownerName() const { - QString name; - auto user = Fm::UserInfoCache::globalInstance()->userFromId(info->uid()); - if(user) { - name = user->name(); - } - return name; -} - -QString FolderModelItem::ownerGroup() const { - auto group = Fm::UserInfoCache::globalInstance()->groupFromId(info->gid()); - return group ? group->name() : QString(); -} - -const QString &FolderModelItem::displayMtime() const { - if(dispMtime_.isEmpty()) { - auto mtime = QDateTime::fromMSecsSinceEpoch(info->mtime() * 1000); - dispMtime_ = mtime.toString(Qt::SystemLocaleShortDate); - } - return dispMtime_; -} - -const QString& FolderModelItem::displaySize() const { - if(!info->isDir()) { - // FIXME: choose IEC or SI units - dispSize_ = Fm::formatFileSize(info->size(), false); - } - return dispSize_; -} - -bool FolderModelItem::isCut() const { - return !cutFilesHashSet_.expired() || info->isCut(); -} - -void FolderModelItem::bindCutFiles(const std::shared_ptr& cutFilesHashSet) { - cutFilesHashSet_ = cutFilesHashSet; -} - -// find thumbnail of the specified size -// The returned thumbnail item is temporary and short-lived -// If you need to use the struct later, copy it to your own struct to keep it. -FolderModelItem::Thumbnail* FolderModelItem::findThumbnail(int size, bool transparent) { - QVector::iterator it; - Thumbnail* transThumb = nullptr; - for(it = thumbnails.begin(); it != thumbnails.end(); ++it) { - if(it->size == size) { - if(it->status != ThumbnailLoaded) { - return it; - } - else { // it->status == ThumbnailLoaded - if(it->transparent == false && transparent == true - && size < 48 /* (dirty) needed only for 'compact' and 'details list' view */ ) { - transThumb = it; // save thumb to add transparency later - } - else { - return it; // an image of the same size and transparency is found - } - } - } - } - if(transThumb) { - QImage image(transThumb->image); - - if(!image.hasAlphaChannel()) { - image = image.convertToFormat(QImage::Format_ARGB32); - } - - // add transparency to image - QPainter p; - p.begin(&image); - p.setCompositionMode(QPainter::CompositionMode_DestinationIn); - p.fillRect(image.rect(), QColor(0, 0, 0, 115 /* alpha 45% */)); - p.end(); - - // add image to thumbnails - Thumbnail thumbnail; - thumbnail.status = ThumbnailLoaded; - thumbnail.image = image; - thumbnail.size = size; - thumbnail.transparent = true; - thumbnails.append(thumbnail); - } - else if(it == thumbnails.end()) { - Thumbnail thumbnail; - thumbnail.status = ThumbnailNotChecked; - thumbnail.size = size; - thumbnail.transparent = false; - thumbnails.append(thumbnail); - } - return &thumbnails.back(); -} - -// remove cached thumbnail of the specified size -void FolderModelItem::removeThumbnail(int size) { - QVector::iterator it; - for(it = thumbnails.begin(); it != thumbnails.end(); ++it) { - if(it->size == size) { // an image of the same size is found - thumbnails.erase(it); - break; - } - } -} - - -} // namespace Fm diff --git a/src/foldermodelitem.h b/src/foldermodelitem.h deleted file mode 100644 index 6ab6f4c..0000000 --- a/src/foldermodelitem.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_FOLDERMODELITEM_H -#define FM_FOLDERMODELITEM_H - -#include "libfmqtglobals.h" -#include -#include -#include -#include -#include - -#include "core/folder.h" - -namespace Fm { - -class LIBFM_QT_API FolderModelItem { -public: - - enum ThumbnailStatus { - ThumbnailNotChecked, - ThumbnailLoading, - ThumbnailLoaded, - ThumbnailFailed - }; - - struct Thumbnail { - int size; - bool transparent; - ThumbnailStatus status; - QImage image; - }; - -public: - explicit FolderModelItem(const std::shared_ptr& _info); - FolderModelItem(const FolderModelItem& other); - virtual ~FolderModelItem(); - - const QString& displayName() const { - return info->displayName(); - } - - QIcon icon(bool transparent = false) const { - const auto i = info->icon(); - return i ? i->qicon(transparent) : QIcon{}; - } - - QString ownerName() const; - - QString ownerGroup() const; - - const QString& displayMtime() const; - - const QString &displaySize() const; - - bool isCut() const; - - void bindCutFiles(const std::shared_ptr& cutFilesHashSet); - - Thumbnail* findThumbnail(int size, bool transparent); - - void removeThumbnail(int size); - - std::shared_ptr info; - mutable QString dispMtime_; - mutable QString dispSize_; - std::weak_ptr cutFilesHashSet_; - QVector thumbnails; -}; - -} - -#endif // FM_FOLDERMODELITEM_H diff --git a/src/folderview.cpp b/src/folderview.cpp deleted file mode 100644 index c9f2f05..0000000 --- a/src/folderview.cpp +++ /dev/null @@ -1,1375 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 "folderview.h" -#include "foldermodel.h" -#include -#include -#include -#include "proxyfoldermodel.h" -#include "folderitemdelegate.h" -#include "dndactionmenu.h" -#include "filemenu.h" -#include "foldermenu.h" -#include "filelauncher.h" -#include "utilities.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // for XDS support -#include // for XDS support -#include "xdndworkaround.h" // for XDS support -#include "folderview_p.h" -#include "utilities.h" - -Q_DECLARE_OPAQUE_POINTER(FmFileInfo*) - -#define SCROLL_FRAMES_PER_SEC 50 -#define SCROLL_DURATION 300 // in ms - -static const int scrollAnimFrames = SCROLL_FRAMES_PER_SEC * SCROLL_DURATION / 1000; - -using namespace Fm; - -FolderViewListView::FolderViewListView(QWidget* parent): - QListView(parent), - activationAllowed_(true) { - connect(this, &QListView::activated, this, &FolderViewListView::activation); - // inline renaming - setEditTriggers(QAbstractItemView::NoEditTriggers); -} - -FolderViewListView::~FolderViewListView() { -} - -void FolderViewListView::startDrag(Qt::DropActions supportedActions) { - if(movement() != Static) { - QListView::startDrag(supportedActions); - } - else { - QAbstractItemView::startDrag(supportedActions); - } -} - -void FolderViewListView::mousePressEvent(QMouseEvent* event) { - QListView::mousePressEvent(event); - static_cast(parent())->childMousePressEvent(event); -} - -void FolderViewListView::mouseMoveEvent(QMouseEvent* event) { - // NOTE: Filter the BACK & FORWARD buttons to not Drag & Drop with them. - // (by default Qt views drag with any button) - if (event->buttons() == Qt::NoButton || event->buttons() & ~(Qt::BackButton | Qt::ForwardButton)) - QListView::mouseMoveEvent(event); -} - -QModelIndex FolderViewListView::indexAt(const QPoint& point) const { - QModelIndex index = QListView::indexAt(point); - // NOTE: QListView has a severe design flaw here. It does hit-testing based on the - // total bound rect of the item. The width of an item is determined by max(icon_width, text_width). - // So if the text label is much wider than the icon, when you click outside the icon but - // the point is still within the outer bound rect, the item is still selected. - // This results in very poor usability. Let's do precise hit-testing here. - // An item is hit only when the point is in the icon or text label. - // If the point is in the bound rectangle but outside the icon or text, it should not be selected. - if(viewMode() == QListView::IconMode && index.isValid()) { - QRect visRect = visualRect(index); // visible area on the screen - FolderItemDelegate* delegate = static_cast(itemDelegateForColumn(FolderModel::ColumnFileName)); - QSize margins = delegate->getMargins(); - QSize _iconSize = iconSize(); - if(point.y() < visRect.top() + margins.height()) { // above icon - return QModelIndex(); - } - else if(point.y() < visRect.top() + margins.height() + _iconSize.height()) { // on the icon area - int iconXMargin = (visRect.width() - _iconSize.width()) / 2; - if(point.x() < (visRect.left() + iconXMargin) || point.x() > (visRect.right() + 1 - iconXMargin)) { - // to the left or right of the icon - return QModelIndex(); - } - } - else { - QSize _textSize = delegate->iconViewTextSize(index); - int textHMargin = (visRect.width() - _textSize.width()) / 2; - if(point.y() > visRect.top() + margins.height() + _iconSize.height() + _textSize.height() // below text - // on the text area but to the left or right of the text - || point.x() < visRect.left() + textHMargin || point.x() > visRect.right() + 1 - textHMargin) { - return QModelIndex(); - } - } - // qDebug() << "visualRect: " << visRect << "point:" << point; - } - return index; -} - - -// NOTE: -// QListView has a problem which I consider a bug or a design flaw. -// When you set movement property to Static, theoratically the icons -// should not be movable. However, if you turned on icon mode, -// the icons becomes freely movable despite the value of movement is Static. -// To overcome this bug, we override all drag handling methods, and -// call QAbstractItemView directly, bypassing QListView. -// In this way, we can workaround the buggy behavior. -// The drag handlers of QListView basically does the same things -// as its parent QAbstractItemView, but it also stores the currently -// dragged item and paint them in the view as needed. -// TODO: I really should file a bug report to Qt developers. - -void FolderViewListView::dragEnterEvent(QDragEnterEvent* event) { - if(movement() != Static) { - QListView::dragEnterEvent(event); - } - else { - QAbstractItemView::dragEnterEvent(event); - } - //qDebug("dragEnterEvent"); - //static_cast(parent())->childDragEnterEvent(event); -} - -void FolderViewListView::dragLeaveEvent(QDragLeaveEvent* e) { - if(movement() != Static) { - QListView::dragLeaveEvent(e); - } - else { - QAbstractItemView::dragLeaveEvent(e); - } - static_cast(parent())->childDragLeaveEvent(e); -} - -void FolderViewListView::dragMoveEvent(QDragMoveEvent* e) { - if(movement() != Static) { - QListView::dragMoveEvent(e); - } - else { - QAbstractItemView::dragMoveEvent(e); - } - static_cast(parent())->childDragMoveEvent(e); -} - -void FolderViewListView::dropEvent(QDropEvent* e) { - - static_cast(parent())->childDropEvent(e); - - if(movement() != Static) { - QListView::dropEvent(e); - } - else { - QAbstractItemView::dropEvent(e); - } -} - -void FolderViewListView::mouseReleaseEvent(QMouseEvent* event) { - bool activationWasAllowed = activationAllowed_; - if((!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this)) || (event->button() != Qt::LeftButton)) { - activationAllowed_ = false; - } - - QListView::mouseReleaseEvent(event); - - activationAllowed_ = activationWasAllowed; -} - -void FolderViewListView::mouseDoubleClickEvent(QMouseEvent* event) { - bool activationWasAllowed = activationAllowed_; - if((style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this)) || (event->button() != Qt::LeftButton)) { - activationAllowed_ = false; - } - - QListView::mouseDoubleClickEvent(event); - - activationAllowed_ = activationWasAllowed; -} - -QModelIndex FolderViewListView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) { - QAbstractItemModel* model_ = model(); - - if(model_ && currentIndex().isValid()) { - FolderView::ViewMode viewMode = static_cast(parent())->viewMode(); - if((viewMode == FolderView::IconMode) || (viewMode == FolderView::ThumbnailMode)) { - int next = (layoutDirection() == Qt::RightToLeft) ? - 1 : 1; - - if(cursorAction == QAbstractItemView::MoveRight) { - return model_->index(currentIndex().row() + next, 0); - } - else if(cursorAction == QAbstractItemView::MoveLeft) { - return model_->index(currentIndex().row() - next, 0); - } - } - } - - return QListView::moveCursor(cursorAction, modifiers); -} - -void FolderViewListView::activation(const QModelIndex& index) { - if(activationAllowed_) { - Q_EMIT activatedFiltered(index); - } -} - -//----------------------------------------------------------------------------- - -FolderViewTreeView::FolderViewTreeView(QWidget* parent): - QTreeView(parent), - doingLayout_(false), - layoutTimer_(nullptr), - activationAllowed_(true) { - - header()->setStretchLastSection(true); - setIndentation(0); - /* the default true value may cause a crash on entering a folder - by double clicking because of the viewport update done by - QTreeView::mouseDoubleClickEvent() (a Qt bug?) */ - setExpandsOnDoubleClick(false); - - connect(this, &QTreeView::activated, this, &FolderViewTreeView::activation); - // don't open editor on double clicking - setEditTriggers(QAbstractItemView::NoEditTriggers); -} - -FolderViewTreeView::~FolderViewTreeView() { - if(layoutTimer_) { - delete layoutTimer_; - } -} - -void FolderViewTreeView::setModel(QAbstractItemModel* model) { - QTreeView::setModel(model); - layoutColumns(); - if(ProxyFolderModel* proxyModel = qobject_cast(model)) { - connect(proxyModel, &ProxyFolderModel::sortFilterChanged, this, &FolderViewTreeView::onSortFilterChanged, - Qt::UniqueConnection); - onSortFilterChanged(); - } -} - -void FolderViewTreeView::mousePressEvent(QMouseEvent* event) { - QTreeView::mousePressEvent(event); - static_cast(parent())->childMousePressEvent(event); -} - -void FolderViewTreeView::mouseMoveEvent(QMouseEvent* event) { - // NOTE: Filter the BACK & FORWARD buttons to not Drag & Drop with them. - // (by default Qt views drag with any button) - if (event->buttons() == Qt::NoButton || event->buttons() & ~(Qt::BackButton | Qt::ForwardButton)) - QTreeView::mouseMoveEvent(event); -} - -void FolderViewTreeView::dragEnterEvent(QDragEnterEvent* event) { - QTreeView::dragEnterEvent(event); - //static_cast(parent())->childDragEnterEvent(event); -} - -void FolderViewTreeView::dragLeaveEvent(QDragLeaveEvent* e) { - QTreeView::dragLeaveEvent(e); - static_cast(parent())->childDragLeaveEvent(e); -} - -void FolderViewTreeView::dragMoveEvent(QDragMoveEvent* e) { - QTreeView::dragMoveEvent(e); - static_cast(parent())->childDragMoveEvent(e); -} - -void FolderViewTreeView::dropEvent(QDropEvent* e) { - static_cast(parent())->childDropEvent(e); - QTreeView::dropEvent(e); -} - -// the default list mode of QListView handles column widths -// very badly (worse than gtk+) and it's not very flexible. -// so, let's handle column widths outselves. -void FolderViewTreeView::layoutColumns() { - // qDebug("layoutColumns"); - if(!model()) { - return; - } - doingLayout_ = true; - QHeaderView* headerView = header(); - // the width that's available for showing the columns. - int availWidth = viewport()->contentsRect().width(); - - // get the width that every column want - int numCols = headerView->count(); - if(numCols > 0) { - int desiredWidth = 0; - int* widths = new int[numCols]; // array to store the widths every column needs - QStyleOptionHeader opt; - opt.initFrom(headerView); - opt.fontMetrics = QFontMetrics(font()); - if (headerView->isSortIndicatorShown()) { - opt.sortIndicator = QStyleOptionHeader::SortDown; - } - QAbstractItemModel* model_ = model(); - int column; - for(column = 0; column < numCols; ++column) { - int columnId = headerView->logicalIndex(column); - // get the size that the column needs - if(model_) { - QVariant data = model_->headerData(columnId, Qt::Horizontal, Qt::DisplayRole); - if(data.isValid()) { - opt.text = data.isValid() ? data.toString() : QString(); - } - } - opt.section = columnId; - widths[column] = qMax(sizeHintForColumn(columnId), - style()->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), headerView).width()); - // compute the total width needed - desiredWidth += widths[column]; - } - - int filenameColumn = headerView->visualIndex(FolderModel::ColumnFileName); - // if the total witdh we want exceeds the available space - if(desiredWidth > availWidth) { - // Compute the width available for the filename column - int filenameAvailWidth = availWidth - desiredWidth + widths[filenameColumn]; - - // Compute the minimum acceptable width for the filename column - int filenameMinWidth = qMin(200, sizeHintForColumn(filenameColumn)); - - if(filenameAvailWidth > filenameMinWidth) { - // Shrink the filename column to the available width - widths[filenameColumn] = filenameAvailWidth; - } - else { - // Set the filename column to its minimum width - widths[filenameColumn] = filenameMinWidth; - } - } - else { - // Fill the extra available space with the filename column - widths[filenameColumn] += availWidth - desiredWidth; - } - - // really do the resizing for every column - for(int column = 0; column < numCols; ++column) { - headerView->resizeSection(headerView->logicalIndex(column), widths[column]); - } - delete []widths; - } - doingLayout_ = false; - - if(layoutTimer_) { - delete layoutTimer_; - layoutTimer_ = nullptr; - } -} - -void FolderViewTreeView::resizeEvent(QResizeEvent* event) { - QAbstractItemView::resizeEvent(event); - // prevent endless recursion. - // When manually resizing columns, at the point where a horizontal scroll - // bar has to be inserted or removed, the vertical size changes, a resize - // event occurs and the column headers are flickering badly if the column - // layout is modified at this point. Therefore only layout the columns if - // the horizontal size changes. - if(!doingLayout_ && event->size().width() != event->oldSize().width()) { - layoutColumns(); // layoutColumns() also triggers resizeEvent - } -} - -void FolderViewTreeView::rowsInserted(const QModelIndex& parent, int start, int end) { - QTreeView::rowsInserted(parent, start, end); - queueLayoutColumns(); -} - -void FolderViewTreeView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) { - QTreeView::rowsAboutToBeRemoved(parent, start, end); - queueLayoutColumns(); -} - -void FolderViewTreeView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles /*= QVector{}*/) { - QTreeView::dataChanged(topLeft, bottomRight, roles); - // FIXME: this will be very inefficient - // queueLayoutColumns(); -} - -void FolderViewTreeView::reset() { - // Sometimes when the content of the model is radically changed, Qt does reset() - // on the model rather than doing large amount of insertion and deletion. - // This is for performance reason so in this case rowsInserted() and rowsAboutToBeRemoved() - // might not be called. Hence we also have to re-layout the columns when the model is reset. - // This fixes bug #190 - // https://github.com/lxqt/pcmanfm-qt/issues/190 - QTreeView::reset(); - queueLayoutColumns(); -} - -void FolderViewTreeView::queueLayoutColumns() { - // qDebug("queueLayoutColumns"); - if(!layoutTimer_) { - layoutTimer_ = new QTimer(); - layoutTimer_->setSingleShot(true); - layoutTimer_->setInterval(0); - connect(layoutTimer_, &QTimer::timeout, this, &FolderViewTreeView::layoutColumns); - } - layoutTimer_->start(); -} - -void FolderViewTreeView::mouseReleaseEvent(QMouseEvent* event) { - bool activationWasAllowed = activationAllowed_; - if((!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this)) || (event->button() != Qt::LeftButton)) { - activationAllowed_ = false; - } - - QTreeView::mouseReleaseEvent(event); - - activationAllowed_ = activationWasAllowed; -} - -void FolderViewTreeView::mouseDoubleClickEvent(QMouseEvent* event) { - bool activationWasAllowed = activationAllowed_; - if((style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this)) || (event->button() != Qt::LeftButton)) { - activationAllowed_ = false; - } - - QTreeView::mouseDoubleClickEvent(event); - - activationAllowed_ = activationWasAllowed; -} - -void FolderViewTreeView::activation(const QModelIndex& index) { - if(activationAllowed_) { - Q_EMIT activatedFiltered(index); - } -} - -void FolderViewTreeView::onSortFilterChanged() { - if(QSortFilterProxyModel* proxyModel = qobject_cast(model())) { - header()->setSortIndicatorShown(true); - header()->setSortIndicator(proxyModel->sortColumn(), proxyModel->sortOrder()); - if(!isSortingEnabled()) { - setSortingEnabled(true); - } - } -} - - -//----------------------------------------------------------------------------- - -FolderView::FolderView(FolderView::ViewMode _mode, QWidget *parent): - QWidget(parent), - view(nullptr), - model_(nullptr), - mode((ViewMode)0), - fileLauncher_(nullptr), - autoSelectionDelay_(600), - autoSelectionTimer_(nullptr), - selChangedTimer_(nullptr), - itemDelegateMargins_(QSize(3, 3)), - smoothScrollTimer_(nullptr), - wheelEvent_(nullptr) { - - iconSize_[IconMode - FirstViewMode] = QSize(48, 48); - iconSize_[CompactMode - FirstViewMode] = QSize(24, 24); - iconSize_[ThumbnailMode - FirstViewMode] = QSize(128, 128); - iconSize_[DetailedListMode - FirstViewMode] = QSize(24, 24); - - QVBoxLayout* layout = new QVBoxLayout(); - layout->setMargin(0); - setLayout(layout); - - setViewMode(_mode); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - connect(this, &FolderView::clicked, this, &FolderView::onFileClicked); - connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &FolderView::onClipboardDataChange); -} - -FolderView::~FolderView() { - if(smoothScrollTimer_) { - disconnect(smoothScrollTimer_, &QTimer::timeout, this, &FolderView::scrollSmoothly); - smoothScrollTimer_->stop(); - delete smoothScrollTimer_; - } -} - -void FolderView::onItemActivated(QModelIndex index) { - if(index.isValid() && index.model()) { - QVariant data = index.model()->data(index, FolderModel::FileInfoRole); - auto info = data.value>(); - if(info) { - if(!(QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))) { - Q_EMIT clicked(ActivatedClick, info); - } - } - } -} - -void FolderView::onSelChangedTimeout() { - selChangedTimer_->deleteLater(); - selChangedTimer_ = nullptr; - // qDebug()<<"selected:" << nSel; - Q_EMIT selChanged(); -} - -void FolderView::onSelectionChanged(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/) { - // It's possible that the selected items change too often and this slot gets called for thousands of times. - // For example, when you select thousands of files and delete them, we will get one selectionChanged() event - // for every deleted file. So, we use a timer to delay the handling to avoid too frequent updates of the UI. - if(!selChangedTimer_) { - selChangedTimer_ = new QTimer(this); - selChangedTimer_->setSingleShot(true); - connect(selChangedTimer_, &QTimer::timeout, this, &FolderView::onSelChangedTimeout); - selChangedTimer_->start(200); - } -} - -void FolderView::onClosingEditor(QWidget* editor, QAbstractItemDelegate::EndEditHint hint) { - if (hint != QAbstractItemDelegate::NoHint) { - // we set the hint to NoHint in FolderItemDelegate::eventFilter() - return; - } - QString newName; - if (qobject_cast(editor)) { // icon and thumbnail view - newName = qobject_cast(editor)->toPlainText(); - } - else if (qobject_cast(editor)) { // compact view - newName = qobject_cast(editor)->text(); - } - if (newName.isEmpty()) { - return; - } - // the editor will be deleted by QAbstractItemDelegate::destroyEditor() when no longer needed - - QModelIndex index = view->selectionModel()->currentIndex(); - if(index.isValid() && index.model()) { - QVariant data = index.model()->data(index, FolderModel::FileInfoRole); - auto info = data.value>(); - if (info) { - auto oldName = QString::fromStdString(info->name()); - if(newName == oldName) { - return; - } - QWidget* parent = window(); - if (window() == this) { // supposedly desktop, in case it uses this - parent = nullptr; - } - changeFileName(info->path(), newName, parent); - } - } -} - -void FolderView::setViewMode(ViewMode _mode) { - if(_mode == mode) { // if it's the same more, ignore - return; - } - // smooth scrolling is only for icon and thumbnail modes - if(smoothScrollTimer_ && (_mode == DetailedListMode || _mode == CompactMode)) { - disconnect(smoothScrollTimer_, &QTimer::timeout, this, &FolderView::scrollSmoothly); - smoothScrollTimer_->stop(); - delete smoothScrollTimer_; - smoothScrollTimer_ = nullptr; - } - // FIXME: retain old selection - - // since only detailed list mode uses QTreeView, and others - // all use QListView, it's wise to preserve QListView when possible. - bool recreateView = false; - if(view && (mode == DetailedListMode || _mode == DetailedListMode)) { - delete view; // FIXME: no virtual dtor? - view = nullptr; - recreateView = true; - } - mode = _mode; - QSize iconSize = iconSize_[mode - FirstViewMode]; - - FolderItemDelegate* delegate = nullptr; - if(mode == DetailedListMode) { - FolderViewTreeView* treeView = new FolderViewTreeView(this); - connect(treeView, &FolderViewTreeView::activatedFiltered, this, &FolderView::onItemActivated); - setFocusProxy(treeView); - - view = treeView; - treeView->setItemsExpandable(false); - treeView->setRootIsDecorated(false); - treeView->setAllColumnsShowFocus(false); - - // set our own custom delegate - delegate = new FolderItemDelegate(treeView); - treeView->setItemDelegateForColumn(FolderModel::ColumnFileName, delegate); - } - else { - FolderViewListView* listView; - if(view) { - listView = static_cast(view); - } - else { - listView = new FolderViewListView(this); - connect(listView, &FolderViewListView::activatedFiltered, this, &FolderView::onItemActivated); - view = listView; - } - setFocusProxy(listView); - - // set our own custom delegate - delegate = new FolderItemDelegate(listView); - listView->setItemDelegateForColumn(FolderModel::ColumnFileName, delegate); - // FIXME: should we expose the delegate? - listView->setMovement(QListView::Static); - /* If listView is already visible, setMovement() will lay out items again with delay - (see Qt, QListView::setMovement(), d->doDelayedItemsLayout()) and thus drop events - will remain disabled for the viewport. So, we should re-enable drop events here. */ - if(listView->viewport()->isVisible()) { - listView->viewport()->setAcceptDrops(true); - } - listView->setResizeMode(QListView::Adjust); - listView->setWrapping(true); - switch(mode) { - case IconMode: { - listView->setViewMode(QListView::IconMode); - listView->setWordWrap(true); - listView->setFlow(QListView::LeftToRight); - break; - } - case CompactMode: { - listView->setViewMode(QListView::ListMode); - listView->setWordWrap(false); - listView->setFlow(QListView::QListView::TopToBottom); - break; - } - case ThumbnailMode: { - listView->setViewMode(QListView::IconMode); - listView->setWordWrap(true); - listView->setFlow(QListView::LeftToRight); - break; - } - default: - ; - } - updateGridSize(); - } - if(view) { - // we have to install the event filter on the viewport instead of the view itself. - view->viewport()->installEventFilter(this); - // we want the QEvent::HoverMove event for single click + auto-selection support - view->viewport()->setAttribute(Qt::WA_Hover, true); - view->setContextMenuPolicy(Qt::NoContextMenu); // defer the context menu handling to parent widgets - view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - view->setIconSize(iconSize); - - view->setSelectionMode(QAbstractItemView::ExtendedSelection); - layout()->addWidget(view); - - // enable dnd (the drop indicator is set at "FolderView::childDragMoveEvent()") - view->setDragEnabled(true); - view->setAcceptDrops(true); - view->setDragDropMode(QAbstractItemView::DragDrop); - - // inline renaming - if(delegate) { - connect(delegate, &QAbstractItemDelegate::closeEditor, this, &FolderView::onClosingEditor); - } - - if(model_) { - // FIXME: preserve selections - model_->setThumbnailSize(iconSize.width()); - view->setModel(model_); - if(recreateView) { - connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FolderView::onSelectionChanged); - } - } - } -} - -// set proper grid size for the QListView based on current view mode, icon size, and font size. -void FolderView::updateGridSize() { - if(mode == DetailedListMode || !view) { - return; - } - FolderViewListView* listView = static_cast(view); - QSize icon = iconSize(mode); // size of the icon - QFontMetrics fm = fontMetrics(); // size of current font - QSize grid; // the final grid size - switch(mode) { - case IconMode: - case ThumbnailMode: { - // NOTE by PCMan about finding the optimal text label size: - // The average filename length on my root filesystem is roughly 18-20 chars. - // So, a reasonable size for the text label is about 10 chars each line since string of this length - // can be shown in two lines. If you consider word wrap, then the result is around 10 chars per word. - // In average, 10 char per line should be enough to display a "word" in the filename without breaking. - // The values can be estimated with this command: - // > find / | xargs basename -a | sed -e s'/[_-]/ /g' | wc -mcw - // However, this average only applies to English. For some Asian characters, such as Chinese chars, - // each char actually takes doubled space. To be safe, we use 13 chars per line x average char width - // to get a nearly optimal width for the text label. As most of the filenames have less than 40 chars - // 13 chars x 3 lines should be enough to show the full filenames for most files. - int textWidth = fm.averageCharWidth() * 13; - int textHeight = fm.lineSpacing() * 3; - grid.setWidth(qMax(icon.width(), textWidth) + 4); // a margin of 2 px for selection rects - grid.setHeight(icon.height() + textHeight + 4); // a margin of 2 px for selection rects - // grow to include margins - grid += 2*itemDelegateMargins_; - // let horizontal and vertical spacings be set only by itemDelegateMargins_ - listView->setSpacing(0); - - break; - } - default: - // FIXME: set proper item size - listView->setSpacing(2); - ; // do not use grid size - } - - FolderItemDelegate* delegate = static_cast(listView->itemDelegateForColumn(FolderModel::ColumnFileName)); - delegate->setItemSize(grid); - delegate->setIconSize(icon); - delegate->setMargins(itemDelegateMargins_); -} - -void FolderView::setIconSize(ViewMode mode, QSize size) { - Q_ASSERT(mode >= FirstViewMode && mode <= LastViewMode); - iconSize_[mode - FirstViewMode] = size; - if(viewMode() == mode) { - view->setIconSize(size); - if(model_) { - model_->setThumbnailSize(size.width()); - } - updateGridSize(); - } -} - -QSize FolderView::iconSize(ViewMode mode) const { - Q_ASSERT(mode >= FirstViewMode && mode <= LastViewMode); - return iconSize_[mode - FirstViewMode]; -} - -void FolderView::setMargins(QSize size) { - if(itemDelegateMargins_ != size.expandedTo(QSize(0, 0))) { - itemDelegateMargins_ = size.expandedTo(QSize(0, 0)); - updateGridSize(); - } -} - -FolderView::ViewMode FolderView::viewMode() const { - return mode; -} - -void FolderView::setAutoSelectionDelay(int delay) { - autoSelectionDelay_ = delay; -} - -QAbstractItemView* FolderView::childView() const { - return view; -} - -ProxyFolderModel* FolderView::model() const { - return model_; -} - -void FolderView::setModel(ProxyFolderModel* model) { - if(view) { - view->setModel(model); - QSize iconSize = iconSize_[mode - FirstViewMode]; - model->setThumbnailSize(iconSize.width()); - if(view->selectionModel()) { - connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FolderView::onSelectionChanged); - } - } - if(model_) { - delete model_; - } - model_ = model; -} - -bool FolderView::event(QEvent* event) { - switch(event->type()) { - case QEvent::StyleChange: - break; - case QEvent::FontChange: - updateGridSize(); - break; - default: - break; - } - return QWidget::event(event); -} - -void FolderView::contextMenuEvent(QContextMenuEvent* event) { - QWidget::contextMenuEvent(event); - QPoint pos = event->pos(); - QPoint view_pos = view->mapFromParent(pos); - QPoint viewport_pos = view->viewport()->mapFromParent(view_pos); - emitClickedAt(ContextMenuClick, viewport_pos); -} - -void FolderView::childMousePressEvent(QMouseEvent* event) { - // called from mousePressEvent() of child view - Qt::MouseButton button = event->button(); - if(button == Qt::MiddleButton) { - emitClickedAt(MiddleClick, event->pos()); - } - else if(button == Qt::BackButton) { - Q_EMIT clickedBack(); - } - else if(button == Qt::ForwardButton) { - Q_EMIT clickedForward(); - } -} - -void FolderView::emitClickedAt(ClickType type, const QPoint& pos) { - // indexAt() needs a point in "viewport" coordinates. - QModelIndex index = view->indexAt(pos); - if(index.isValid()) { - QVariant data = index.data(FolderModel::FileInfoRole); - auto info = data.value>(); - Q_EMIT clicked(type, info); - } - else { - // FIXME: should we show popup menu for the selected files instead - // if there are selected files? - if(type == ContextMenuClick) { - // clear current selection if clicked outside selected files - view->clearSelection(); - Q_EMIT clicked(type, nullptr); - } - } -} - -QModelIndexList FolderView::selectedRows(int column) const { - QItemSelectionModel* selModel = selectionModel(); - if(selModel) { - return selModel->selectedRows(column); - } - return QModelIndexList(); -} - -// This returns all selected "cells", which means all cells of the same row are returned. -QModelIndexList FolderView::selectedIndexes() const { - QItemSelectionModel* selModel = selectionModel(); - if(selModel) { - return selModel->selectedIndexes(); - } - return QModelIndexList(); -} - -QItemSelectionModel* FolderView::selectionModel() const { - return view ? view->selectionModel() : nullptr; -} - -Fm::FilePathList FolderView::selectedFilePaths() const { - if(model_) { - QModelIndexList selIndexes = mode == DetailedListMode ? selectedRows() : selectedIndexes(); - if(!selIndexes.isEmpty()) { - Fm::FilePathList paths; - QModelIndexList::const_iterator it; - for(it = selIndexes.constBegin(); it != selIndexes.constEnd(); ++it) { - auto file = model_->fileInfoFromIndex(*it); - paths.push_back(file->path()); - } - return paths; - } - } - return Fm::FilePathList(); -} - -bool FolderView::hasSelection() const { - QItemSelectionModel* selModel = selectionModel(); - return selModel ? selModel->hasSelection() : false; -} - -QModelIndex FolderView::indexFromFolderPath(const Fm::FilePath& folderPath) const { - if(!model_ || !folderPath.isValid()) { - return QModelIndex(); - } - QModelIndex index; - int count = model_->rowCount(); - for(int row = 0; row < count; ++row) { - index = model_->index(row, 0); - auto info = model_->fileInfoFromIndex(index); - if(info && info->isDir() && folderPath == info->path()) { - return index; - } - } - return QModelIndex(); -} - -void FolderView::selectFiles(const Fm::FileInfoList& files, bool add) { - if(!model_ || files.empty()) { - return; - } - if(!add) { - selectionModel()->clear(); - } - QModelIndex index, firstIndex; - int count = model_->rowCount(); - Fm::FileInfoList list = files; - bool singleFile(files.size() == 1); - for(int row = 0; row < count; ++row) { - if (list.empty()) { - break; - } - index = model_->index(row, 0); - auto info = model_->fileInfoFromIndex(index); - for(auto it = list.cbegin(); it != list.cend(); ++it) { - auto& item = *it; - if(item == info) { - selectionModel()->select(index, QItemSelectionModel::Select); - if (!firstIndex.isValid()) { - firstIndex = index; - } - list.erase(it); - break; - } - } - } - if (firstIndex.isValid()) { - view->scrollTo(firstIndex, QAbstractItemView::EnsureVisible); - if (singleFile) { // give focus to the single file - selectionModel()->setCurrentIndex(firstIndex, QItemSelectionModel::Current); - } - } -} - -Fm::FileInfoList FolderView::selectedFiles() const { - if(model_) { - QModelIndexList selIndexes = mode == DetailedListMode ? selectedRows() : selectedIndexes(); - if(!selIndexes.isEmpty()) { - Fm::FileInfoList files; - QModelIndexList::const_iterator it; - for(it = selIndexes.constBegin(); it != selIndexes.constEnd(); ++it) { - auto file = model_->fileInfoFromIndex(*it); - files.push_back(file); - } - return files; - } - } - return Fm::FileInfoList(); -} - -void FolderView::selectAll() { - if(mode == DetailedListMode) { - view->selectAll(); - } - else { - // NOTE: By default QListView::selectAll() selects all columns in the model. - // However, QListView only show the first column. Normal selection by mouse - // can only select the first column of every row. I consider this discripancy yet - // another design flaw of Qt. To make them consistent, we do it ourselves by only - // selecting the first column of every row and do not select all columns as Qt does. - // I'll report a Qt bug for this later. - if(model_) { - const QItemSelection sel{model_->index(0, 0), model_->index(model_->rowCount() - 1, 0)}; - selectionModel()->select(sel, QItemSelectionModel::Select); - } - } -} - -void FolderView::invertSelection() { - if(model_) { - QItemSelectionModel* selModel = view->selectionModel(); - int rows = model_->rowCount(); - QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Toggle; - if(mode == DetailedListMode) { - flags |= QItemSelectionModel::Rows; - } - for(int row = 0; row < rows; ++row) { - QModelIndex index = model_->index(row, 0); - selModel->select(index, flags); - } - } -} - -void FolderView::childDragEnterEvent(QDragEnterEvent* event) { - //qDebug("drag enter"); - if(event->mimeData()->hasFormat("text/uri-list")) { - event->accept(); - } - else { - event->ignore(); - } -} - -void FolderView::childDragLeaveEvent(QDragLeaveEvent* e) { - //qDebug("drag leave"); - e->accept(); -} - -void FolderView::childDragMoveEvent(QDragMoveEvent* e) { - // Since it isn't possible to drop on a file (see "FolderModel::dropMimeData()"), - // we enable the drop indicator only when the cursor is on a folder. - QModelIndex index = view->indexAt(e->pos()); - if(index.isValid() && index.model()) { - QVariant data = index.model()->data(index, FolderModel::FileInfoRole); - auto info = data.value>(); - if(info && !info->isDir()) { - view->setDropIndicatorShown(false); - return; - } - } - view->setDropIndicatorShown(true); -} - -void FolderView::childDropEvent(QDropEvent* e) { - // qDebug("drop"); - // Try to support XDS - // NOTE: in theory, it's not possible to implement XDS with pure Qt. - // We achieved this with some dirty XCB/XDND workarounds. - // Please refer to XdndWorkaround::clientMessage() in xdndworkaround.cpp for details. - if(QX11Info::isPlatformX11() && e->mimeData()->hasFormat("XdndDirectSave0")) { - e->setDropAction(Qt::CopyAction); - const QWidget* targetWidget = childView()->viewport(); - // these are dynamic QObject property set by our XDND workarounds in xdndworkaround.cpp. - xcb_window_t dndSource = xcb_window_t(targetWidget->property("xdnd::lastDragSource").toUInt()); - //xcb_timestamp_t dropTimestamp = (xcb_timestamp_t)targetWidget->property("xdnd::lastDropTime").toUInt(); - // qDebug() << "XDS: source window" << dndSource << dropTimestamp; - if(dndSource != 0) { - xcb_atom_t XdndDirectSaveAtom = XdndWorkaround::internAtom("XdndDirectSave0", 15); - xcb_atom_t textAtom = XdndWorkaround::internAtom("text/plain", 10); - - // 1. get the filename from XdndDirectSave property of the source window - QByteArray basename = XdndWorkaround::windowProperty(dndSource, XdndDirectSaveAtom, textAtom, 1024); - - // 2. construct the fill URI for the file, and update the source window property. - Fm::FilePath filePath; - if(model_) { - QModelIndex index = view->indexAt(e->pos()); - auto info = model_->fileInfoFromIndex(index); - if(info && info->isDir()) { - filePath = info->path().child(basename); - } - } - if(!filePath.isValid()) { - filePath = path().child(basename); - } - QByteArray fileUri = filePath.uri().get(); - XdndWorkaround::setWindowProperty(dndSource, XdndDirectSaveAtom, textAtom, (void*)fileUri.constData(), fileUri.length()); - - // 3. send to XDS selection data request with type "XdndDirectSave" to the source window and - // receive result from the source window. (S: success, E: error, or F: failure) - QByteArray result = e->mimeData()->data("XdndDirectSave0"); - // NOTE: there seems to be some bugs in file-roller so it always replies with "E" even if the - // file extraction is finished successfully. Anyways, we ignore any error at the moment. - } - e->accept(); // yeah! we've done with XDS so stop Qt from further event propagation. - return; - } - - if(e->keyboardModifiers() == Qt::NoModifier) { - // if no key modifiers are used, popup a menu - // to ask the user for the action he/she wants to perform. - Qt::DropAction action = DndActionMenu::askUser(e->possibleActions(), QCursor::pos()); - e->setDropAction(action); - } -} - -bool FolderView::eventFilter(QObject* watched, QEvent* event) { - // NOTE: Instead of simply filtering the drag and drop events of the child view in - // the event filter, we overrided each event handler virtual methods in - // both QListView and QTreeView and added some childXXXEvent() callbacks. - // We did this because of a design flaw of Qt. - // All QAbstractScrollArea derived widgets, including QAbstractItemView - // contains an internal child widget, which is called a viewport. - // The events actually comes from the child viewport, not the parent view itself. - // Qt redirects the events of viewport to the viewportEvent() method of - // QAbstractScrollArea and let the parent widget handle the events. - // Qt implemented this using a event filter installed on the child viewport widget. - // That means, when we try to install an event filter on the viewport, - // there is already a filter installed by Qt which will be called before ours. - // So we can never intercept the event handling of QAbstractItemView by using a filter. - // That's why we override respective virtual methods for different events. - if(view && watched == view->viewport()) { - switch(event->type()) { - case QEvent::HoverMove: - // activate items on single click - if(style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) { - QHoverEvent* hoverEvent = static_cast(event); - QModelIndex index = view->indexAt(hoverEvent->pos()); // find out the hovered item - if(index.isValid()) { // change the cursor to a hand when hovering on an item - setCursor(Qt::PointingHandCursor); - } - else { - setCursor(Qt::ArrowCursor); - } - // turn on auto-selection for hovered item when single click mode is used. - if(autoSelectionDelay_ > 0 && model_) { - if(!autoSelectionTimer_) { - autoSelectionTimer_ = new QTimer(this); - connect(autoSelectionTimer_, &QTimer::timeout, this, &FolderView::onAutoSelectionTimeout); - lastAutoSelectionIndex_ = QModelIndex(); - } - autoSelectionTimer_->start(autoSelectionDelay_); - } - } - break; - case QEvent::HoverLeave: - if(style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) { - setCursor(Qt::ArrowCursor); - } - break; - case QEvent::Wheel: - // don't let the view scroll during an inline renaming - if (view) { - FolderItemDelegate* delegate = nullptr; - if(mode == DetailedListMode) { - FolderViewTreeView* treeView = static_cast(view); - delegate = static_cast(treeView->itemDelegateForColumn(FolderModel::ColumnFileName)); - } - else { - FolderViewListView* listView = static_cast(view); - delegate = static_cast(listView->itemDelegateForColumn(FolderModel::ColumnFileName)); - } - if (delegate && delegate->hasEditor()) { - return true; - } - } - // This is to fix #85: Scrolling doesn't work in compact view - // Actually, I think it's the bug of Qt, not ours. - // When in compact mode, only the horizontal scroll bar is used and the vertical one is hidden. - // So, when a user scroll his mouse wheel, it's reasonable to scroll the horizontal scollbar. - // Qt does not implement such a simple feature, unfortunately. - // We do it by forwarding the scroll event in the viewport to the horizontal scrollbar. - // FIXME: if someday Qt supports this, we have to disable the workaround. - if(mode == CompactMode) { - QScrollBar* scroll = view->horizontalScrollBar(); - if(scroll) { - QApplication::sendEvent(scroll, event); - return true; - } - } - // Smooth Scrolling - // Some tricks are adapted from . - else if(mode != DetailedListMode - && event->spontaneous() - && !(QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::AltModifier))) { - if(QScrollBar* vbar = view->verticalScrollBar()) { - // keep track of the wheel event for smooth scrolling - wheelEvent_ = static_cast(event); - int delta = wheelEvent_->angleDelta().y(); - if((delta > 0 && vbar->value() == vbar->minimum()) || (delta < 0 && vbar->value() == vbar->maximum())) { - break; // the scrollbar can't move - } - // get a rough estimation of the wheel speed and disable animation if it's too high - static QList wheelEvents; - wheelEvents << QDateTime::currentMSecsSinceEpoch(); - while(wheelEvents.last() - wheelEvents.first() > 500) { - wheelEvents.removeFirst(); - } - if(wheelEvents.size() > 10) { - break; - } - - if(!smoothScrollTimer_) { - smoothScrollTimer_ = new QTimer(); - connect(smoothScrollTimer_, &QTimer::timeout, this, &FolderView::scrollSmoothly); - } - - // set the data for smooth scrolling - scollData data; - data.delta = delta; - data.leftFrames = scrollAnimFrames; - queuedScrollSteps_.append(data); - smoothScrollTimer_->start(1000 / SCROLL_FRAMES_PER_SEC); - return true; - } - } - break; - default: - break; - } - } - return QObject::eventFilter(watched, event); -} - -void FolderView::scrollSmoothly() { - if(!wheelEvent_ || !view->verticalScrollBar()) { - return; - } - - int totalDelta = 0; - QList::iterator it = queuedScrollSteps_.begin(); - while(it != queuedScrollSteps_.end()) { - if(it->leftFrames == 1) { // find the exact delta for the last frame - totalDelta += it->delta - (scrollAnimFrames - 1) * qRound((qreal)it->delta / (qreal)scrollAnimFrames); - it = queuedScrollSteps_.erase(it); - } - else { - totalDelta += qRound((qreal)it->delta / (qreal)scrollAnimFrames); - -- it->leftFrames; - ++it; - } - } - if(totalDelta != 0) { - // as in qevent.cpp -> QWheelEvent::QWheelEvent() - QWheelEvent e(wheelEvent_->pos(), wheelEvent_->globalPos(), - totalDelta, - wheelEvent_->buttons(), Qt::NoModifier, Qt::Vertical); - QApplication::sendEvent(view->verticalScrollBar(), &e); - } - if(queuedScrollSteps_.empty()) { - smoothScrollTimer_->stop(); - } -} - -// this slot handles auto-selection of items. -void FolderView::onAutoSelectionTimeout() { - if(QApplication::mouseButtons() != Qt::NoButton) { - return; - } - - Qt::KeyboardModifiers mods = QApplication::keyboardModifiers(); - QPoint pos = view->viewport()->mapFromGlobal(QCursor::pos()); // convert to viewport coordinates - QModelIndex index = view->indexAt(pos); // find out the hovered item - QItemSelectionModel::SelectionFlags flags = (mode == DetailedListMode ? QItemSelectionModel::Rows : QItemSelectionModel::NoUpdate); - QItemSelectionModel* selModel = view->selectionModel(); - - if(mods & Qt::ControlModifier) { // Ctrl key is pressed - if(selModel->isSelected(index) && index != lastAutoSelectionIndex_) { - // unselect a previously selected item - selModel->select(index, flags | QItemSelectionModel::Deselect); - lastAutoSelectionIndex_ = QModelIndex(); - } - else { - // select an unselected item - selModel->select(index, flags | QItemSelectionModel::Select); - lastAutoSelectionIndex_ = index; - } - selModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); // move the cursor - } - else if(mods & Qt::ShiftModifier) { // Shift key is pressed - // select all items between current index and the hovered index. - QModelIndex current = selModel->currentIndex(); - if(selModel->hasSelection() && current.isValid()) { - selModel->clear(); // clear old selection - selModel->setCurrentIndex(current, QItemSelectionModel::NoUpdate); - int begin = current.row(); - int end = index.row(); - if(begin > end) { - qSwap(begin, end); - } - for(int row = begin; row <= end; ++row) { - QModelIndex sel = model_->index(row, 0); - selModel->select(sel, flags | QItemSelectionModel::Select); - } - } - else { // no items are selected, select the hovered item. - if(index.isValid()) { - selModel->select(index, flags | QItemSelectionModel::SelectCurrent); - selModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); - } - } - lastAutoSelectionIndex_ = index; - } - else if(mods == Qt::NoModifier) { // no modifier keys are pressed. - if(index.isValid()) { - // select the hovered item - view->clearSelection(); - selModel->select(index, flags | QItemSelectionModel::SelectCurrent); - selModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); - } - lastAutoSelectionIndex_ = index; - } - - autoSelectionTimer_->deleteLater(); - autoSelectionTimer_ = nullptr; -} - -void FolderView::onFileClicked(int type, const std::shared_ptr &fileInfo) { - if(type == ActivatedClick) { - if(fileLauncher_) { - Fm::FileInfoList files; - files.push_back(fileInfo); - fileLauncher_->launchFiles(nullptr, std::move(files)); - } - } - else if(type == ContextMenuClick) { - Fm::FilePath folderPath; - bool isWritableDir(true); - auto files = selectedFiles(); - if(!files.empty()) { - auto& first = files.front(); - if(files.size() == 1 && first->isDir()) { - folderPath = first->path(); - isWritableDir = first->isWritable(); - } - } - if(!folderPath.isValid()) { - folderPath = path(); - if(auto info = folderInfo()) { - isWritableDir = info->isWritable(); - } - } - QMenu* menu = nullptr; - if(fileInfo) { - // show context menu - auto files = selectedFiles(); - if(!files.empty()) { - QModelIndexList selIndexes = mode == DetailedListMode ? selectedRows() : selectedIndexes(); - Fm::FileMenu* fileMenu = (view && selIndexes.size() == 1) - ? new Fm::FileMenu(files, fileInfo, folderPath, isWritableDir, QString(), view) - : new Fm::FileMenu(files, fileInfo, folderPath, isWritableDir); - fileMenu->setFileLauncher(fileLauncher_); - prepareFileMenu(fileMenu); - menu = fileMenu; - } - } - else if (folderInfo()) { - Fm::FolderMenu* folderMenu = new Fm::FolderMenu(this); - prepareFolderMenu(folderMenu); - menu = folderMenu; - } - if(menu) { - menu->exec(QCursor::pos()); - delete menu; - } - } -} - -void FolderView::onClipboardDataChange() { - if(model_) { - const QClipboard* clipboard = QApplication::clipboard(); - const QMimeData* data = clipboard->mimeData(); - Fm::FilePathList paths; - bool isCutSelection; - std::tie(paths, isCutSelection) = Fm::parseClipboardData(*data); - if(!folder()->path().hasUriScheme("search") // skip for search results - && isCutSelection - && Fm::isCurrentPidClipboardData(*data)) { // set cut files only with this app - auto cutDirPath = paths.size() > 0 ? paths[0].parent() : FilePath(); - // set the cut file(s) only if the cutting is done here - if(folder()->path() == cutDirPath - && selectedFilePaths() == paths) { - model_->setCutFiles(selectionModel()->selection()); - } - else if(folder()->hadCutFilesUnset() || folder()->hasCutFiles()) { - model_->setCutFiles(QItemSelection()); - } - return; - } - - folder()->setCutFiles(std::make_shared()); // clean Folder::cutFilesHashSet_ - if(folder()->hadCutFilesUnset()) { - model_->setCutFiles(QItemSelection()); // update indexes if there were cut files here - } - } -} - -void FolderView::prepareFileMenu(FileMenu* /*menu*/) { -} - -void FolderView::prepareFolderMenu(FolderMenu* /*menu*/) { -} diff --git a/src/folderview.h b/src/folderview.h deleted file mode 100644 index 3ea50a3..0000000 --- a/src/folderview.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_FOLDERVIEW_H -#define FM_FOLDERVIEW_H - -#include "libfmqtglobals.h" -#include -#include -#include -#include -#include -#include "foldermodel.h" -#include "proxyfoldermodel.h" - -#include "core/folder.h" - -class QTimer; - -namespace Fm { - -class FileMenu; -class FolderMenu; -class FileLauncher; -class FolderViewStyle; - -class LIBFM_QT_API FolderView : public QWidget { - Q_OBJECT - -public: - - enum ViewMode { - FirstViewMode = 1, - IconMode = FirstViewMode, - CompactMode, - DetailedListMode, - ThumbnailMode, - LastViewMode = ThumbnailMode, - NumViewModes = (LastViewMode - FirstViewMode + 1) - }; - - enum ClickType { - ActivatedClick, - MiddleClick, - ContextMenuClick - }; - - friend class FolderViewTreeView; - friend class FolderViewListView; - - explicit FolderView(ViewMode _mode = IconMode, QWidget* parent = 0); - - explicit FolderView(QWidget* parent): FolderView{IconMode, parent} {} - - virtual ~FolderView(); - - void setViewMode(ViewMode _mode); - ViewMode viewMode() const; - - void setIconSize(ViewMode mode, QSize size); - QSize iconSize(ViewMode mode) const; - - QAbstractItemView* childView() const; - - ProxyFolderModel* model() const; - void setModel(ProxyFolderModel* _model); - - std::shared_ptr folder() const { - return model_ ? static_cast(model_->sourceModel())->folder() : nullptr; - } - - std::shared_ptr folderInfo() const { - auto _folder = folder(); - return _folder ? _folder->info() : nullptr; - } - - Fm::FilePath path() { - auto _folder = folder(); - return _folder ? _folder->path() : Fm::FilePath(); - } - - QItemSelectionModel* selectionModel() const; - Fm::FileInfoList selectedFiles() const; - Fm::FilePathList selectedFilePaths() const; - bool hasSelection() const; - QModelIndex indexFromFolderPath(const Fm::FilePath& folderPath) const; - void selectFiles(const Fm::FileInfoList& files, bool add = false); - - void selectAll(); - - void invertSelection(); - - void setFileLauncher(FileLauncher* launcher) { - fileLauncher_ = launcher; - } - - FileLauncher* fileLauncher() { - return fileLauncher_; - } - - int autoSelectionDelay() const { - return autoSelectionDelay_; - } - - void setAutoSelectionDelay(int delay); - -protected: - virtual bool event(QEvent* event); - virtual void contextMenuEvent(QContextMenuEvent* event); - virtual void childMousePressEvent(QMouseEvent* event); - virtual void childDragEnterEvent(QDragEnterEvent* event); - virtual void childDragMoveEvent(QDragMoveEvent* e); - virtual void childDragLeaveEvent(QDragLeaveEvent* e); - virtual void childDropEvent(QDropEvent* e); - - void emitClickedAt(ClickType type, const QPoint& pos); - - QModelIndexList selectedRows(int column = 0) const; - QModelIndexList selectedIndexes() const; - - virtual void prepareFileMenu(Fm::FileMenu* menu); - virtual void prepareFolderMenu(Fm::FolderMenu* menu); - - virtual bool eventFilter(QObject* watched, QEvent* event); - - void updateGridSize(); // called when view mode, icon size, font size or cell margin is changed - - QSize getMargins() const { - return itemDelegateMargins_; - } - - // sets the cell margins in the icon and thumbnail modes - // and calls updateGridSize() when needed - void setMargins(QSize size); - -public Q_SLOTS: - void onItemActivated(QModelIndex index); - void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); - virtual void onFileClicked(int type, const std::shared_ptr& fileInfo); - void onClipboardDataChange(); - -private Q_SLOTS: - void onAutoSelectionTimeout(); - void onSelChangedTimeout(); - void onClosingEditor(QWidget* editor, QAbstractItemDelegate::EndEditHint hint); - void scrollSmoothly(); - -Q_SIGNALS: - void clicked(int type, const std::shared_ptr& file); - void clickedBack(); - void clickedForward(); - void selChanged(); - void sortChanged(); - -private: - - QAbstractItemView* view; - ProxyFolderModel* model_; - ViewMode mode; - QSize iconSize_[NumViewModes]; - FileLauncher* fileLauncher_; - int autoSelectionDelay_; - QTimer* autoSelectionTimer_; - QModelIndex lastAutoSelectionIndex_; - QTimer* selChangedTimer_; - // the cell margins in the icon and thumbnail modes - QSize itemDelegateMargins_; - // smooth scrolling: - struct scollData { - int delta; - int leftFrames; - }; - QTimer *smoothScrollTimer_; - QWheelEvent *wheelEvent_; - QList queuedScrollSteps_; -}; - -} - -#endif // FM_FOLDERVIEW_H diff --git a/src/folderview_p.h b/src/folderview_p.h deleted file mode 100644 index 769bb38..0000000 --- a/src/folderview_p.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_FOLDERVIEW_P_H -#define FM_FOLDERVIEW_P_H - -#include -#include -#include -#include "folderview.h" - -class QTimer; - -namespace Fm { - -// override these classes for implementing FolderView -class FolderViewListView : public QListView { - Q_OBJECT -public: - friend class FolderView; - FolderViewListView(QWidget* parent = 0); - virtual ~FolderViewListView(); - virtual void startDrag(Qt::DropActions supportedActions); - virtual void mousePressEvent(QMouseEvent* event); - virtual void mouseMoveEvent(QMouseEvent* event); - virtual void mouseReleaseEvent(QMouseEvent* event); - virtual void mouseDoubleClickEvent(QMouseEvent* event); - virtual void dragEnterEvent(QDragEnterEvent* event); - virtual void dragMoveEvent(QDragMoveEvent* e); - virtual void dragLeaveEvent(QDragLeaveEvent* e); - virtual void dropEvent(QDropEvent* e); - - virtual QModelIndex indexAt(const QPoint & point) const; - - inline void setPositionForIndex(const QPoint & position, const QModelIndex & index) { - QListView::setPositionForIndex(position, index); - } - - inline QRect rectForIndex(const QModelIndex & index) const { - return QListView::rectForIndex(index); - } - - inline QStyleOptionViewItem getViewOptions() { - return viewOptions(); - } - -Q_SIGNALS: - void activatedFiltered(const QModelIndex &index); - -protected: - virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers); - -private Q_SLOTS: - void activation(const QModelIndex &index); - -private: - bool activationAllowed_; -}; - -class FolderViewTreeView : public QTreeView { - Q_OBJECT -public: - friend class FolderView; - FolderViewTreeView(QWidget* parent = 0); - virtual ~FolderViewTreeView(); - virtual void setModel(QAbstractItemModel* model); - virtual void mousePressEvent(QMouseEvent* event); - virtual void mouseMoveEvent(QMouseEvent* event); - virtual void mouseReleaseEvent(QMouseEvent* event); - virtual void mouseDoubleClickEvent(QMouseEvent* event); - virtual void dragEnterEvent(QDragEnterEvent* event); - virtual void dragMoveEvent(QDragMoveEvent* e); - virtual void dragLeaveEvent(QDragLeaveEvent* e); - virtual void dropEvent(QDropEvent* e); - - virtual void rowsInserted(const QModelIndex& parent,int start, int end); - virtual void rowsAboutToBeRemoved(const QModelIndex& parent,int start, int end); - virtual void dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles = QVector{}); - virtual void reset(); - - virtual void resizeEvent(QResizeEvent* event); - void queueLayoutColumns(); - -Q_SIGNALS: - void activatedFiltered(const QModelIndex &index); - -private Q_SLOTS: - void layoutColumns(); - void activation(const QModelIndex &index); - void onSortFilterChanged(); - -private: - bool doingLayout_; - QTimer* layoutTimer_; - bool activationAllowed_; -}; - - -} // namespace Fm - -#endif // FM_FOLDERVIEW_P_H diff --git a/src/fontbutton.cpp b/src/fontbutton.cpp deleted file mode 100644 index f3b21a6..0000000 --- a/src/fontbutton.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 "fontbutton.h" -#include -#include - -namespace Fm { - -FontButton::FontButton(QWidget* parent): QPushButton(parent) { - connect(this, &QPushButton::clicked, this, &FontButton::onClicked); -} - -FontButton::~FontButton() { -} - -void FontButton::onClicked() { - QFontDialog dlg(font_); - if(dlg.exec() == QDialog::Accepted) { - setFont(dlg.selectedFont()); - } -} - -void FontButton::setFont(QFont font) { - font_ = font; - QString text = font.family(); - if(font.bold()) { - text += " "; - text += tr("Bold"); - } - if(font.italic()) { - text += " "; - text += tr("Italic"); - } - text += QString(" %1").arg(font.pointSize()); - setText(text); - Q_EMIT changed(); -} - - -} // namespace Fm diff --git a/src/fontbutton.h b/src/fontbutton.h deleted file mode 100644 index 4dee7b2..0000000 --- a/src/fontbutton.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_FONTBUTTON_H -#define FM_FONTBUTTON_H - -#include "libfmqtglobals.h" -#include - - -namespace Fm { - -class LIBFM_QT_API FontButton : public QPushButton { - Q_OBJECT -public: - explicit FontButton(QWidget* parent = 0); - virtual ~FontButton(); - - QFont font() { - return font_; - } - - void setFont(QFont font); - -Q_SIGNALS: - void changed(); - -private Q_SLOTS: - void onClicked(); - -private: - QFont font_; -}; - -} - -#endif // FM_FONTBUTTON_H diff --git a/src/libfm-qt.pc.in b/src/libfm-qt.pc.in deleted file mode 100644 index 2ca5c17..0000000 --- a/src/libfm-qt.pc.in +++ /dev/null @@ -1,12 +0,0 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -exec_prefix=${prefix} -libdir=${exec_prefix}/lib -includedir=${prefix}/include - -Name: libfm-qt -Description: A Qt/glib/gio-based lib used to develop file managers providing some file management utilities. (This is a Qt port of the original libfm library) -URL: https://github.com/lxqt/libfm-qt -Requires: @REQUIRED_QT@ libfm >= 1.2.0 -Version: @LIBFM_QT_VERSION@ -Libs: -L${libdir} -lfm -l@LIBFM_QT_LIBRARY_NAME@ -Cflags: -I${includedir} diff --git a/src/libfmqt.cpp b/src/libfmqt.cpp deleted file mode 100644 index 2b01643..0000000 --- a/src/libfmqt.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 -#include "libfmqt.h" -#include -#include -#include "core/thumbnailer.h" -#include "xdndworkaround.h" - -namespace Fm { - -struct LibFmQtData { - LibFmQtData(); - ~LibFmQtData(); - - QTranslator translator; - XdndWorkaround workaround; - int refCount; - Q_DISABLE_COPY(LibFmQtData) -}; - -static LibFmQtData* theLibFmData = nullptr; - -static GFile* lookupCustomUri(GVfs * /*vfs*/, const char *identifier, gpointer /*user_data*/) { - GFile* gf = fm_file_new_for_uri(identifier); - return gf; -} - -LibFmQtData::LibFmQtData(): refCount(1) { -#if !GLIB_CHECK_VERSION(2, 36, 0) - g_type_init(); -#endif - fm_init(nullptr); - // turn on glib debug message - // g_setenv("G_MESSAGES_DEBUG", "all", true); - Fm::Thumbnailer::loadAll(); - translator.load("libfm-qt_" + QLocale::system().name(), LIBFM_QT_DATA_DIR "/translations"); - - // register some URI schemes implemented by libfm - // FIXME: move these implementations into libfm-qt to avoid linking with libfm. - GVfs* vfs = g_vfs_get_default(); - g_vfs_register_uri_scheme(vfs, "menu", lookupCustomUri, nullptr, nullptr, lookupCustomUri, nullptr, nullptr); - g_vfs_register_uri_scheme(vfs, "search", lookupCustomUri, nullptr, nullptr, lookupCustomUri, nullptr, nullptr); -} - -LibFmQtData::~LibFmQtData() { - GVfs* vfs = g_vfs_get_default(); - g_vfs_unregister_uri_scheme(vfs, "menu"); - g_vfs_unregister_uri_scheme(vfs, "search"); - fm_finalize(); -} - -LibFmQt::LibFmQt() { - if(!theLibFmData) { - theLibFmData = new LibFmQtData(); - } - else { - ++theLibFmData->refCount; - } - d = theLibFmData; -} - -LibFmQt::~LibFmQt() { - if(--d->refCount == 0) { - delete d; - theLibFmData = nullptr; - } -} - -QTranslator* LibFmQt::translator() { - return &d->translator; -} - -} // namespace Fm diff --git a/src/libfmqt.h b/src/libfmqt.h deleted file mode 100644 index e63e2db..0000000 --- a/src/libfmqt.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_APPLICATION_H -#define FM_APPLICATION_H - -#include "libfmqtglobals.h" -#include -#include -#include - -namespace Fm { - -struct LibFmQtData; - -class LIBFM_QT_API LibFmQt { -public: - explicit LibFmQt(); - ~LibFmQt(); - - QTranslator* translator(); - -private: - LibFmQt(LibFmQt& other); // disable copy - LibFmQtData* d; -}; - -} - -#endif // FM_APPLICATION_H diff --git a/src/libfmqtglobals.h b/src/libfmqtglobals.h deleted file mode 100644 index 1a6d4fe..0000000 --- a/src/libfmqtglobals.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_GLOBALS_ -#define _LIBFM_QT_GLOBALS_ - -#include "fm-qt_export.h" - -#endif diff --git a/src/mount-operation-password.ui b/src/mount-operation-password.ui deleted file mode 100644 index 72f1b30..0000000 --- a/src/mount-operation-password.ui +++ /dev/null @@ -1,215 +0,0 @@ - - - MountOperationPasswordDialog - - - - 0 - 0 - 244 - 302 - - - - - 0 - 0 - - - - Mount - - - - - - false - - - false - - - - - - - 0 - 0 - - - - - - - - - - - Connect &anonymously - - - usernameGroup - - - - - - - Connect as u&ser: - - - usernameGroup - - - - - - - - - - - - - 0 - 0 - - - - &Username: - - - username - - - - - - - QLineEdit::Password - - - - - - - - 0 - 0 - - - - &Password: - - - password - - - - - - - &Domain: - - - domain - - - - - - - - - - - - Forget password &immediately - - - passwordGroup - - - - - - - Remember password until you &logout - - - passwordGroup - - - - - - - Remember &forever - - - passwordGroup - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - Anonymous - asUser - username - domain - password - forgetPassword - sessionPassword - storePassword - - - - - buttonBox - accepted() - MountOperationPasswordDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - MountOperationPasswordDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - - - - - diff --git a/src/mountoperation.cpp b/src/mountoperation.cpp deleted file mode 100644 index 5502da4..0000000 --- a/src/mountoperation.cpp +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 "mountoperation.h" -#include // for _() -#include -#include -#include -#include "mountoperationpassworddialog_p.h" -#include "mountoperationquestiondialog_p.h" -#include "ui_mount-operation-password.h" -#include "core/gioptrs.h" - -namespace Fm { - -MountOperation::MountOperation(bool interactive, QWidget* parent): - QObject(parent), - op(g_mount_operation_new()), - cancellable_(g_cancellable_new()), - running(false), - interactive_(interactive), - eventLoop(nullptr), - autoDestroy_(true) { - - g_signal_connect(op, "ask-password", G_CALLBACK(onAskPassword), this); - g_signal_connect(op, "ask-question", G_CALLBACK(onAskQuestion), this); - // g_signal_connect(op, "reply", G_CALLBACK(onReply), this); - -#if GLIB_CHECK_VERSION(2, 20, 0) - g_signal_connect(op, "aborted", G_CALLBACK(onAbort), this); -#endif -#if GLIB_CHECK_VERSION(2, 22, 0) - g_signal_connect(op, "show-processes", G_CALLBACK(onShowProcesses), this); -#endif -#if GLIB_CHECK_VERSION(2, 34, 0) - g_signal_connect(op, "show-unmount-progress", G_CALLBACK(onShowUnmountProgress), this); -#endif - -} - -MountOperation::~MountOperation() { - qDebug("delete MountOperation"); - if(cancellable_) { - cancel(); - g_object_unref(cancellable_); - } - - if(eventLoop) { // if wait() is called to block the main loop, but the event loop is still running - // NOTE: is this possible? - eventLoop->exit(1); - } - - if(op) { - g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onAskPassword), this); - g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onAskQuestion), this); -#if GLIB_CHECK_VERSION(2, 20, 0) - g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onAbort), this); -#endif -#if GLIB_CHECK_VERSION(2, 22, 0) - g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onShowProcesses), this); -#endif -#if GLIB_CHECK_VERSION(2, 34, 0) - g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onShowUnmountProgress), this); -#endif - g_object_unref(op); - } - // qDebug("MountOperation deleted"); -} - -void MountOperation::mountEnclosingVolume(const FilePath &path) { - g_file_mount_enclosing_volume(path.gfile().get(), G_MOUNT_MOUNT_NONE, op, cancellable_, - (GAsyncReadyCallback)onMountFileFinished, new QPointer(this)); -} - -void MountOperation::mountMountable(const FilePath &mountable) { - g_file_mount_mountable(mountable.gfile().get(), G_MOUNT_MOUNT_NONE, op, cancellable_, - (GAsyncReadyCallback)onMountMountableFinished, new QPointer(this)); -} - -void MountOperation::onAbort(GMountOperation* /*_op*/, MountOperation* /*pThis*/) { - -} - -void MountOperation::onAskPassword(GMountOperation* /*_op*/, gchar* message, gchar* default_user, gchar* default_domain, GAskPasswordFlags flags, MountOperation* pThis) { - qDebug("ask password"); - MountOperationPasswordDialog dlg(pThis, flags); - dlg.setMessage(QString::fromUtf8(message)); - dlg.setDefaultUser(QString::fromUtf8(default_user)); - dlg.setDefaultDomain(QString::fromUtf8(default_domain)); - dlg.exec(); -} - -void MountOperation::onAskQuestion(GMountOperation* /*_op*/, gchar* message, GStrv choices, MountOperation* pThis) { - qDebug("ask question"); - MountOperationQuestionDialog dialog(pThis, message, choices); - dialog.exec(); -} - -/* -void MountOperation::onReply(GMountOperation* _op, GMountOperationResult result, MountOperation* pThis) { - qDebug("reply"); -} -*/ - -void MountOperation::onShowProcesses(GMountOperation* /*_op*/, gchar* /*message*/, GArray* /*processes*/, GStrv /*choices*/, MountOperation* /*pThis*/) { - qDebug("show processes"); -} - -void MountOperation::onShowUnmountProgress(GMountOperation* /*_op*/, gchar* /*message*/, gint64 /*time_left*/, gint64 /*bytes_left*/, MountOperation* /*pThis*/) { - qDebug("show unmount progress"); -} - -void MountOperation::onEjectMountFinished(GMount* mount, GAsyncResult* res, QPointer< MountOperation >* pThis) { - if(*pThis) { - GError* error = nullptr; - g_mount_eject_with_operation_finish(mount, res, &error); - (*pThis)->handleFinish(error); - } - delete pThis; -} - -void MountOperation::onEjectVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer< MountOperation >* pThis) { - if(*pThis) { - GError* error = nullptr; - g_volume_eject_with_operation_finish(volume, res, &error); - (*pThis)->handleFinish(error); - } - delete pThis; -} - -void MountOperation::onMountFileFinished(GFile* file, GAsyncResult* res, QPointer< MountOperation >* pThis) { - if(*pThis) { - GError* error = nullptr; - g_file_mount_enclosing_volume_finish(file, res, &error); - (*pThis)->handleFinish(error); - } - delete pThis; -} - -void MountOperation::onMountMountableFinished(GFile* file, GAsyncResult* res, QPointer* pThis) { - if(*pThis) { - GError* error = nullptr; - g_file_mount_mountable_finish(file, res, &error); - (*pThis)->handleFinish(error); - } - delete pThis; -} - -void MountOperation::onMountVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer< MountOperation >* pThis) { - if(*pThis) { - GError* error = nullptr; - g_volume_mount_finish(volume, res, &error); - (*pThis)->handleFinish(error); - } - delete pThis; -} - -void MountOperation::onUnmountMountFinished(GMount* mount, GAsyncResult* res, QPointer< MountOperation >* pThis) { - if(*pThis) { - GError* error = nullptr; - g_mount_unmount_with_operation_finish(mount, res, &error); - (*pThis)->handleFinish(error); - } - delete pThis; -} - -void MountOperation::handleFinish(GError* error) { - qDebug("operation finished: %p", static_cast(error)); - if(error) { - bool showError = interactive_; - if(error->domain == G_IO_ERROR) { - if(error->code == G_IO_ERROR_FAILED) { - // Generate a more human-readable error message instead of using a gvfs one. - // The original error message is something like: - // Error unmounting: umount exited with exit code 1: - // helper failed with: umount: only root can unmount - // UUID=18cbf00c-e65f-445a-bccc-11964bdea05d from /media/sda4 */ - // Why they pass this back to us? This is not human-readable for the users at all. - if(strstr(error->message, "only root can ")) { - g_free(error->message); - error->message = g_strdup(_("Only system administrators have the permission to do this.")); - } - } - else if(error->code == G_IO_ERROR_FAILED_HANDLED) { - showError = false; - } - } - if(showError) { - QMessageBox::critical(nullptr, QObject::tr("Error"), QString::fromUtf8(error->message)); - } - } - - Q_EMIT finished(error); - - if(eventLoop) { // if wait() is called to block the main loop - eventLoop->exit(error != nullptr ? 1 : 0); - eventLoop = nullptr; - } - - if(error) { - g_error_free(error); - } - - // free ourself here!! - if(autoDestroy_) { - deleteLater(); - } -} - -void MountOperation::prepareUnmount(GMount* mount) { - /* ensure that CWD is not on the mounted filesystem. */ - char* cwd_str = g_get_current_dir(); - GFile* cwd = g_file_new_for_path(cwd_str); - GFile* root = g_mount_get_root(mount); - g_free(cwd_str); - /* FIXME: This cannot cover 100% cases since symlinks are not checked. - * There may be other cases that cwd is actually under mount root - * but checking prefix is not enough. We already did our best, though. */ - if(g_file_has_prefix(cwd, root)) { - g_chdir("/"); - } - g_object_unref(cwd); - g_object_unref(root); -} - -// block the operation used an internal QEventLoop and returns -// only after the whole operation is finished. -bool MountOperation::wait() { - QEventLoop loop; - eventLoop = &loop; - int exitCode = loop.exec(); - return exitCode == 0 ? true : false; -} - -} // namespace Fm diff --git a/src/mountoperation.h b/src/mountoperation.h deleted file mode 100644 index 5587508..0000000 --- a/src/mountoperation.h +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_MOUNTOPERATION_H -#define FM_MOUNTOPERATION_H - -#include "libfmqtglobals.h" -#include -#include -#include -#include -#include - -#include "core/filepath.h" - -class QEventLoop; - -namespace Fm { - -// FIXME: the original APIs in gtk+ version of libfm for mounting devices is poor. -// Need to find a better API design which make things fully async and cancellable. - -// FIXME: parent_ does not work. All dialogs shown by the mount operation has no parent window assigned. -// FIXME: Need to reconsider the propery way of API design. Blocking sync calls are handy, but -// indeed causes some problems. :-( - -class LIBFM_QT_API MountOperation: public QObject { - Q_OBJECT - -public: - explicit MountOperation(bool interactive = true, QWidget* parent = 0); - ~MountOperation(); - - FM_QT_DEPRECATED - void mount(const Fm::FilePath& path) { - mountEnclosingVolume(path); - } - - void mountEnclosingVolume(const Fm::FilePath& path); - - void mountMountable(const Fm::FilePath& mountable); - - void mount(GVolume* volume) { - g_volume_mount(volume, G_MOUNT_MOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onMountVolumeFinished, new QPointer(this)); - } - - void unmount(GMount* mount) { - prepareUnmount(mount); - g_mount_unmount_with_operation(mount, G_MOUNT_UNMOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onUnmountMountFinished, new QPointer(this)); - } - - void unmount(GVolume* volume) { - GMount* mount = g_volume_get_mount(volume); - if(!mount) { - return; - } - unmount(mount); - g_object_unref(mount); - } - - void eject(GMount* mount) { - prepareUnmount(mount); - g_mount_eject_with_operation(mount, G_MOUNT_UNMOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onEjectMountFinished, new QPointer(this)); - } - - void eject(GVolume* volume) { - GMount* mnt = g_volume_get_mount(volume); - prepareUnmount(mnt); - g_object_unref(mnt); - g_volume_eject_with_operation(volume, G_MOUNT_UNMOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onEjectVolumeFinished, new QPointer(this)); - } - - QWidget* parent() const { - return parent_; - } - - void setParent(QWidget* parent) { - parent_ = parent; - } - - GCancellable* cancellable() const { - return cancellable_; - } - - GMountOperation* mountOperation() { - return op; - } - - void cancel() { - g_cancellable_cancel(cancellable_); - } - - bool isRunning() const { - return running; - } - - // block the operation used an internal QEventLoop and returns - // only after the whole operation is finished. - bool wait(); - - bool autoDestroy() { - return autoDestroy_; - } - - void setAutoDestroy(bool destroy = true) { - autoDestroy_ = destroy; - } - -Q_SIGNALS: - void finished(GError* error = nullptr); - -private: - void prepareUnmount(GMount* mount); - - static void onAskPassword(GMountOperation* _op, gchar* message, gchar* default_user, gchar* default_domain, GAskPasswordFlags flags, MountOperation* pThis); - static void onAskQuestion(GMountOperation* _op, gchar* message, GStrv choices, MountOperation* pThis); - // static void onReply(GMountOperation *_op, GMountOperationResult result, MountOperation* pThis); - - static void onAbort(GMountOperation* _op, MountOperation* pThis); - static void onShowProcesses(GMountOperation* _op, gchar* message, GArray* processes, GStrv choices, MountOperation* pThis); - static void onShowUnmountProgress(GMountOperation* _op, gchar* message, gint64 time_left, gint64 bytes_left, MountOperation* pThis); - - // it's possible that this object is freed when the callback is called by gio, so guarding with QPointer is needed here. - static void onMountFileFinished(GFile* file, GAsyncResult* res, QPointer* pThis); - static void onMountMountableFinished(GFile* file, GAsyncResult* res, QPointer* pThis); - static void onMountVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer* pThis); - static void onUnmountMountFinished(GMount* mount, GAsyncResult* res, QPointer* pThis); - static void onEjectMountFinished(GMount* mount, GAsyncResult* res, QPointer* pThis); - static void onEjectVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer* pThis); - - void handleFinish(GError* error); - -private: - GMountOperation* op; - GCancellable* cancellable_; - QWidget* parent_; - bool running; - bool interactive_; - QEventLoop* eventLoop; - bool autoDestroy_; -}; - -} - -#endif // FM_MOUNTOPERATION_H diff --git a/src/mountoperationpassworddialog.cpp b/src/mountoperationpassworddialog.cpp deleted file mode 100644 index 4e7d850..0000000 --- a/src/mountoperationpassworddialog.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 "mountoperationpassworddialog_p.h" -#include "ui_mount-operation-password.h" -#include "mountoperation.h" - -namespace Fm { - -MountOperationPasswordDialog::MountOperationPasswordDialog(MountOperation* op, GAskPasswordFlags flags): - QDialog(), - mountOperation(op), - needPassword(flags & G_ASK_PASSWORD_NEED_PASSWORD ? true : false), - needUserName(flags & G_ASK_PASSWORD_NEED_USERNAME ? true : false), - needDomain(flags & G_ASK_PASSWORD_NEED_DOMAIN ? true : false), - canSavePassword(flags & G_ASK_PASSWORD_SAVING_SUPPORTED ? true : false), - canAnonymous(flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED ? true : false) { - - ui = new Ui::MountOperationPasswordDialog(); - ui->setupUi(this); - - // change the text of Ok button to Connect - ui->buttonBox->buttons().constFirst()->setText(tr("&Connect")); - connect(ui->Anonymous, &QAbstractButton::toggled, this, &MountOperationPasswordDialog::onAnonymousToggled); - - if(canAnonymous) { - // select ananymous by default if applicable. - ui->Anonymous->setChecked(true); - } - else { - ui->Anonymous->setEnabled(false); - ui->asUser->setChecked(true); - } - if(!needUserName) { - ui->username->setEnabled(false); - } - if(needPassword) { - if(!needUserName) { - ui->password->setFocus(); - } - } - else { - ui->password->setEnabled(false); - } - if(!needDomain) { - ui->domain->hide(); - ui->domainLabel->hide(); - } - if(canSavePassword) { - ui->sessionPassword->setChecked(true); - } - else { - ui->storePassword->setEnabled(false); - ui->sessionPassword->setEnabled(false); - ui->forgetPassword->setChecked(true); - } -} - -MountOperationPasswordDialog::~MountOperationPasswordDialog() { - delete ui; -} - -void MountOperationPasswordDialog::onAnonymousToggled(bool checked) { - // disable username/password entries if anonymous mode is used - bool useUserPassword = !checked; - if(needUserName) { - ui->username->setEnabled(useUserPassword); - } - if(needPassword) { - ui->password->setEnabled(useUserPassword); - } - if(needDomain) { - ui->domain->setEnabled(useUserPassword); - } - - if(canSavePassword) { - ui->forgetPassword->setEnabled(useUserPassword); - ui->sessionPassword->setEnabled(useUserPassword); - ui->storePassword->setEnabled(useUserPassword); - } -} - -void MountOperationPasswordDialog::setMessage(QString message) { - ui->message->setText(message); -} - -void MountOperationPasswordDialog::setDefaultDomain(QString domain) { - ui->domain->setText(domain); -} - -void MountOperationPasswordDialog::setDefaultUser(QString user) { - ui->username->setText(user); -} - -void MountOperationPasswordDialog::done(int r) { - GMountOperation* gmop = mountOperation->mountOperation(); - - if(r == QDialog::Accepted) { - - if(needUserName) { - g_mount_operation_set_username(gmop, ui->username->text().toUtf8()); - } - if(needDomain) { - g_mount_operation_set_domain(gmop, ui->domain->text().toUtf8()); - } - if(needPassword) { - g_mount_operation_set_password(gmop, ui->password->text().toUtf8()); - } - if(canAnonymous) { - g_mount_operation_set_anonymous(gmop, ui->Anonymous->isChecked()); - } - - g_mount_operation_reply(gmop, G_MOUNT_OPERATION_HANDLED); - } - else { - g_mount_operation_reply(gmop, G_MOUNT_OPERATION_ABORTED); - } - QDialog::done(r); -} - -} // namespace Fm diff --git a/src/mountoperationpassworddialog_p.h b/src/mountoperationpassworddialog_p.h deleted file mode 100644 index 1fd6cd1..0000000 --- a/src/mountoperationpassworddialog_p.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_MOUNTOPERATIONPASSWORDDIALOG_H -#define FM_MOUNTOPERATIONPASSWORDDIALOG_H - -#include "libfmqtglobals.h" -#include -#include - -namespace Ui { -class MountOperationPasswordDialog; -} - -namespace Fm { - -class MountOperation; - -class MountOperationPasswordDialog : public QDialog { - Q_OBJECT - -public: - explicit MountOperationPasswordDialog(MountOperation* op, GAskPasswordFlags flags); - virtual ~MountOperationPasswordDialog(); - - void setMessage(QString message); - void setDefaultUser(QString user); - void setDefaultDomain(QString domain); - - virtual void done(int r); - -private Q_SLOTS: - void onAnonymousToggled(bool checked); - -private: - Ui::MountOperationPasswordDialog* ui; - MountOperation* mountOperation; - bool needPassword; - bool needUserName; - bool needDomain; - bool canSavePassword; - bool canAnonymous; -}; - -} - -#endif // FM_MOUNTOPERATIONPASSWORDDIALOG_H diff --git a/src/mountoperationquestiondialog.cpp b/src/mountoperationquestiondialog.cpp deleted file mode 100644 index 3679740..0000000 --- a/src/mountoperationquestiondialog.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 "mountoperationquestiondialog_p.h" -#include "mountoperation.h" -#include - -namespace Fm { - -MountOperationQuestionDialog::MountOperationQuestionDialog(MountOperation* op, gchar* message, GStrv choices): - QMessageBox(), - mountOperation(op) { - - setIcon(QMessageBox::Question); - setText(QString::fromUtf8(message)); - - choiceCount = g_strv_length(choices); - choiceButtons = new QAbstractButton*[choiceCount]; - for(int i = 0; i < choiceCount; ++i) { - // It's not allowed to add custom buttons without standard roles - // to QMessageBox. So we set role of all buttons to AcceptRole. - // When any of the set buttons is clicked, exec() always returns "accept". - QPushButton* button = new QPushButton(QString::fromUtf8(choices[i])); - addButton(button, QMessageBox::AcceptRole); - choiceButtons[i] = button; - } -} - -MountOperationQuestionDialog::~MountOperationQuestionDialog() { - delete []choiceButtons; -} - -void MountOperationQuestionDialog::done(int r) { - GMountOperation* op = mountOperation->mountOperation(); - - g_mount_operation_set_choice(op, r); - g_mount_operation_reply(op, G_MOUNT_OPERATION_HANDLED); - - QDialog::done(r); -} - -void MountOperationQuestionDialog::closeEvent(QCloseEvent *event) -{ - GMountOperation* op = mountOperation->mountOperation(); - - g_mount_operation_reply(op, G_MOUNT_OPERATION_ABORTED); - - event->accept(); -} - -} // namespace Fm diff --git a/src/mountoperationquestiondialog_p.h b/src/mountoperationquestiondialog_p.h deleted file mode 100644 index 8c6a810..0000000 --- a/src/mountoperationquestiondialog_p.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_MOUNTOPERATIONQUESTIONDIALOG_H -#define FM_MOUNTOPERATIONQUESTIONDIALOG_H - -#include "libfmqtglobals.h" -#include -#include -#include - -namespace Fm { - -class MountOperation; - -class MountOperationQuestionDialog : public QMessageBox { - Q_OBJECT -public: - MountOperationQuestionDialog(MountOperation* op, gchar* message, GStrv choices); - virtual ~MountOperationQuestionDialog(); - - virtual void done(int r); - virtual void closeEvent(QCloseEvent *event); - -private: - MountOperation* mountOperation; - QAbstractButton** choiceButtons; - int choiceCount; -}; - -} - -#endif // FM_MOUNTOPERATIONQUESTIONDIALOG_H diff --git a/src/pathbar.cpp b/src/pathbar.cpp deleted file mode 100644 index 558627a..0000000 --- a/src/pathbar.cpp +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright (C) 2016 Hong Jen Yee (PCMan) - * - * 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 "pathbar.h" -#include "pathbar_p.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pathedit.h" - - -namespace Fm { - -PathBar::PathBar(QWidget* parent): - QWidget(parent), - tempPathEdit_(nullptr) { - - QHBoxLayout* topLayout = new QHBoxLayout(this); - topLayout->setContentsMargins(0, 0, 0, 0); - topLayout->setSpacing(0); - bool rtl(layoutDirection() == Qt::RightToLeft); - - // the arrow button used to scroll to start of the path - scrollToStart_ = new QToolButton(this); - scrollToStart_->setArrowType(rtl ? Qt::RightArrow : Qt::LeftArrow); - scrollToStart_->setAutoRepeat(true); - scrollToStart_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); - connect(scrollToStart_, &QToolButton::clicked, this, &PathBar::onScrollButtonClicked); - topLayout->addWidget(scrollToStart_); - - // there might be too many buttons when the path is long, so make it scrollable. - scrollArea_ = new QScrollArea(this); - scrollArea_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - scrollArea_->setFrameShape(QFrame::NoFrame); - scrollArea_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - scrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - scrollArea_->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); - scrollArea_->verticalScrollBar()->setDisabled(true); - connect(scrollArea_->horizontalScrollBar(), &QAbstractSlider::valueChanged, this, &PathBar::setArrowEnabledState); - topLayout->addWidget(scrollArea_, 1); // stretch factor=1, make it expandable - - // the arrow button used to scroll to end of the path - scrollToEnd_ = new QToolButton(this); - scrollToEnd_->setArrowType(rtl ? Qt::LeftArrow : Qt::RightArrow); - scrollToEnd_->setAutoRepeat(true); - scrollToEnd_->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); - connect(scrollToEnd_, &QToolButton::clicked, this, &PathBar::onScrollButtonClicked); - topLayout->addWidget(scrollToEnd_); - - // container widget of the path buttons - buttonsWidget_ = new QWidget(this); - buttonsWidget_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - - buttonsLayout_ = new QHBoxLayout(buttonsWidget_); - buttonsLayout_->setContentsMargins(0, 0, 0, 0); - buttonsLayout_->setSpacing(0); - buttonsLayout_->setSizeConstraint(QLayout::SetFixedSize); // required when added to scroll area according to QScrollArea doc. - scrollArea_->setWidget(buttonsWidget_); // make the buttons widget scrollable if the path is too long -} - -void PathBar::resizeEvent(QResizeEvent* event) { - QWidget::resizeEvent(event); - updateScrollButtonVisibility(); -} - -void PathBar::wheelEvent(QWheelEvent* event) { - QWidget::wheelEvent(event); - QAbstractSlider::SliderAction action = QAbstractSlider::SliderNoAction; - int vDelta = event->angleDelta().y(); - if(vDelta > 0) { - if(scrollToStart_->isEnabled()) { - action = QAbstractSlider::SliderSingleStepSub; - } - } - else if(vDelta < 0) { - if(scrollToEnd_->isEnabled()) { - action = QAbstractSlider::SliderSingleStepAdd; - } - } - scrollArea_->horizontalScrollBar()->triggerAction(action); -} - -void PathBar::mousePressEvent(QMouseEvent* event) { - QWidget::mousePressEvent(event); - if(event->button() == Qt::LeftButton) { - openEditor(); - } - else if(event->button() == Qt::MiddleButton) { - PathButton* btn = qobject_cast(childAt(event->x(), event->y())); - if(btn != nullptr) { - scrollArea_->ensureWidgetVisible(btn, - 1); // a harmless compensation for a miscalculation in Qt - Q_EMIT middleClickChdir(pathForButton(btn)); - } - } -} - -void PathBar::contextMenuEvent(QContextMenuEvent* event) { - QMenu* menu = new QMenu(this); - connect(menu, &QMenu::aboutToHide, menu, &QMenu::deleteLater); - - QAction* action = menu->addAction(tr("&Edit Path")); - connect(action, &QAction::triggered, this, &PathBar::openEditor); - - action = menu->addAction(tr("&Copy Path")); - connect(action, &QAction::triggered, this, &PathBar::copyPath); - - menu->popup(mapToGlobal(event->pos())); -} - -void PathBar::updateScrollButtonVisibility() { - // Wait for the horizontal scrollbar to be completely shaped. - // Without this, the enabled state of arrow buttons might be - // wrong when the pathbar is created for the first time. - QTimer::singleShot(0, this, SLOT(setScrollButtonVisibility())); -} - -void PathBar::setScrollButtonVisibility() { - bool showScrollers; - if(tempPathEdit_ != nullptr) { - showScrollers = false; - } - else { - showScrollers = (buttonsLayout_->sizeHint().width() > width()); - } - scrollToStart_->setVisible(showScrollers); - scrollToEnd_->setVisible(showScrollers); - if(showScrollers) { - QScrollBar* sb = scrollArea_->horizontalScrollBar(); - int value = sb->value(); - scrollToStart_->setEnabled(value != sb->minimum()); - scrollToEnd_->setEnabled(value != sb->maximum()); - } -} - -Fm::FilePath PathBar::pathForButton(PathButton* btn) { - std::string fullPath; - int buttonCount = buttonsLayout_->count() - 1; // the last item is a spacer - for(int i = 0; i < buttonCount; ++i) { - if(!fullPath.empty() && fullPath.back() != '/') { - fullPath += '/'; - } - PathButton* elem = static_cast(buttonsLayout_->itemAt(i)->widget()); - fullPath += elem->name(); - if(elem == btn) - break; - } - return Fm::FilePath::fromPathStr(fullPath.c_str()); -} - -void PathBar::onButtonToggled(bool checked) { - if(checked) { - PathButton* btn = static_cast(sender()); - currentPath_ = pathForButton(btn); - Q_EMIT chdir(currentPath_); - - // since scrolling to the toggled buton will happen correctly only when the - // layout is updated and because the update is disabled on creating buttons - // in setPath(), the update status can be used as a sign to know when to wait - if(updatesEnabled()) { - scrollArea_->ensureWidgetVisible(btn, 1); - } - else { - QTimer::singleShot(0, this, SLOT(ensureToggledVisible())); - } - } -} - -void PathBar::ensureToggledVisible() { - int buttonCount = buttonsLayout_->count() - 1; // the last item is a spacer - for(int i = buttonCount - 1; i >= 0; --i) { - if(auto btn = static_cast(buttonsLayout_->itemAt(i)->widget())) { - if(btn->isChecked()) { - scrollArea_->ensureWidgetVisible(btn, 1); - return; - } - } - } -} - -void PathBar::onScrollButtonClicked() { - QToolButton* btn = static_cast(sender()); - QAbstractSlider::SliderAction action = QAbstractSlider::SliderNoAction; - if(btn == scrollToEnd_) { - action = QAbstractSlider::SliderSingleStepAdd; - } - else if(btn == scrollToStart_) { - action = QAbstractSlider::SliderSingleStepSub; - } - scrollArea_->horizontalScrollBar()->triggerAction(action); -} - -void PathBar::setPath(Fm::FilePath path) { - if(currentPath_ == path) { // same path, do nothing - return; - } - - auto oldPath = std::move(currentPath_); - currentPath_ = std::move(path); - // check if we already have a button for this path - int buttonCount = buttonsLayout_->count() - 1; // the last item is a spacer - if(oldPath && currentPath_.isPrefixOf(oldPath)) { - for(int i = buttonCount - 1; i >= 0; --i) { - auto btn = static_cast(buttonsLayout_->itemAt(i)->widget()); - if(pathForButton(btn) == currentPath_) { - btn->setChecked(true); // toggle the button - /* we don't need to emit chdir signal here since later - * toggled signal will be triggered on the button, which - * in turns emit chdir. */ - return; - } - } - } - - /* FIXME: if the new path is the subdir of our full path, actually - * we can append several new buttons rather than re-create - * all of the buttons. This can reduce flickers. */ - - setUpdatesEnabled(false); - // we do not have the path in the buttons list - // destroy existing path element buttons and the spacer - QLayoutItem* item; - while((item = buttonsLayout_->takeAt(0)) != nullptr) { - delete item->widget(); - delete item; - } - - // create new buttons for the new path - auto btnPath = currentPath_; - while(btnPath) { - Fm::CStrPtr name; - Fm::CStrPtr displayName; - auto parent = btnPath.parent(); - // FIXME: some buggy uri types, such as menu://, fail to return NULL when there is no parent path. - // Instead, the path itself is returned. So we check if the parent path is the same as current path. - auto isRoot = !parent.isValid() || parent == btnPath; - if(isRoot) { - displayName = btnPath.displayName(); - name = btnPath.toString(); - } - else { - name = btnPath.baseName(); - } - auto btn = new PathButton(name.get(), displayName ? displayName.get() : name.get(), isRoot, buttonsWidget_); - btn->show(); - connect(btn, &QAbstractButton::toggled, this, &PathBar::onButtonToggled); - buttonsLayout_->insertWidget(0, btn); - if(isRoot) { // this is the root element of the path - break; - } - btnPath = parent; - } - buttonsLayout_->addStretch(1); // add a spacer at the tail of the buttons - - // we don't want to scroll vertically. make the scroll area fit the height of the buttons - // FIXME: this is a little bit hackish :-( - scrollArea_->setFixedHeight(buttonsLayout_->sizeHint().height()); - updateScrollButtonVisibility(); - - // to guarantee that the button will be scrolled to correctly, - // it should be toggled only after the layout update starts above - buttonCount = buttonsLayout_->count() - 1; - if(buttonCount > 0) { - PathButton* lastBtn = static_cast(buttonsLayout_->itemAt(buttonCount - 1)->widget()); - // we don't have to emit the chdir signal since the "onButtonToggled()" slot will be triggered by this. - lastBtn->setChecked(true); - } - - setUpdatesEnabled(true); -} - -void PathBar::openEditor() { - if(tempPathEdit_ == nullptr) { - tempPathEdit_ = new PathEdit(this); - delete layout()->replaceWidget(scrollArea_, tempPathEdit_, Qt::FindDirectChildrenOnly); - scrollArea_->hide(); - scrollToStart_->setVisible(false); - scrollToEnd_->setVisible(false); - tempPathEdit_->setText(currentPath_.toString().get()); - - connect(tempPathEdit_, &PathEdit::returnPressed, this, &PathBar::onReturnPressed); - connect(tempPathEdit_, &PathEdit::editingFinished, this, &PathBar::closeEditor); - } - tempPathEdit_->selectAll(); - QApplication::clipboard()->setText(tempPathEdit_->text(), QClipboard::Selection); - QTimer::singleShot(0, tempPathEdit_, SLOT(setFocus())); -} - -void PathBar::closeEditor() { - if(tempPathEdit_ == nullptr) { - return; - } - // If a menu has popped up synchronously (with QMenu::exec), the path buttons may be drawn - // but the path-edit may not disappear until the menu is closed. So, we hide it here. - tempPathEdit_->setVisible(false); - delete layout()->replaceWidget(tempPathEdit_, scrollArea_, Qt::FindDirectChildrenOnly); - scrollArea_->show(); - if(buttonsLayout_->sizeHint().width() > width()) { - scrollToStart_->setVisible(true); - scrollToEnd_->setVisible(true); - } - - tempPathEdit_->deleteLater(); - tempPathEdit_ = nullptr; - updateScrollButtonVisibility(); - - Q_EMIT editingFinished(); -} - -void PathBar::copyPath() { - QApplication::clipboard()->setText(currentPath_.toString().get()); -} - -void PathBar::onReturnPressed() { - QByteArray pathStr = tempPathEdit_->text().toLocal8Bit(); - setPath(Fm::FilePath::fromPathStr(pathStr.constData())); -} - -void PathBar::setArrowEnabledState(int value) { - if(buttonsLayout_->sizeHint().width() > width()) { - QScrollBar* sb = scrollArea_->horizontalScrollBar(); - scrollToStart_->setEnabled(value != sb->minimum()); - scrollToEnd_->setEnabled(value != sb->maximum()); - } -} - -} // namespace Fm diff --git a/src/pathbar.h b/src/pathbar.h deleted file mode 100644 index 34609fd..0000000 --- a/src/pathbar.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2016 Hong Jen Yee (PCMan) - * - * 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_PATHBAR_H -#define FM_PATHBAR_H - -#include "libfmqtglobals.h" -#include -#include "core/filepath.h" - -class QToolButton; -class QScrollArea; -class QPushButton; -class QHBoxLayout; - -namespace Fm { - -class PathEdit; -class PathButton; - -class LIBFM_QT_API PathBar: public QWidget { - Q_OBJECT -public: - explicit PathBar(QWidget* parent = 0); - - const Fm::FilePath& path() { - return currentPath_; - } - - void setPath(Fm::FilePath path); - -Q_SIGNALS: - void chdir(const Fm::FilePath& path); - void middleClickChdir(const Fm::FilePath& path); - void editingFinished(); - -public Q_SLOTS: - void openEditor(); - void closeEditor(); - void copyPath(); - -private Q_SLOTS: - void onButtonToggled(bool checked); - void onScrollButtonClicked(); - void onReturnPressed(); - void setArrowEnabledState(int value); - void setScrollButtonVisibility(); - void ensureToggledVisible(); - -protected: - void resizeEvent(QResizeEvent* event); - void wheelEvent(QWheelEvent* event); - void mousePressEvent(QMouseEvent* event); - void contextMenuEvent(QContextMenuEvent* event); - -private: - void updateScrollButtonVisibility(); - Fm::FilePath pathForButton(PathButton* btn); - -private: - QToolButton* scrollToStart_; - QToolButton* scrollToEnd_; - QScrollArea* scrollArea_; - QWidget* buttonsWidget_; - QHBoxLayout* buttonsLayout_; - PathEdit* tempPathEdit_; - - Fm::FilePath currentPath_; // currently active path -}; - -} // namespace Fm - -#endif // FM_PATHBAR_H diff --git a/src/pathbar_p.h b/src/pathbar_p.h deleted file mode 100644 index 6a64b98..0000000 --- a/src/pathbar_p.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2016 Hong Jen Yee (PCMan) - * - * 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_PATHBAR_P_H -#define FM_PATHBAR_P_H - -#include -#include -#include -#include -#include -#include - -namespace Fm { - -class PathButton: public QToolButton { - Q_OBJECT -public: - PathButton(std::string name, QString displayName, bool isRoot = false, QWidget* parent = nullptr): - QToolButton(parent), - name_{name} { - - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding); - setCheckable(true); - setAutoExclusive(true); - setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - /* respect the toolbar icon size (can be set with some styles) */ - int icnSize = style()->pixelMetric(QStyle::PM_ToolBarIconSize); - setIconSize(QSize(icnSize, icnSize)); - - setText(displayName); - - if(isRoot) { /* this element is root */ - QIcon icon = QIcon::fromTheme("drive-harddisk"); - setIcon(icon); - } - } - - void changeEvent(QEvent* event) override { - QToolButton::changeEvent(event); - if(event->type() == QEvent::StyleChange) { - int icnSize = style()->pixelMetric(QStyle::PM_ToolBarIconSize); - setIconSize(QSize(icnSize, icnSize)); - } - } - - std::string name() const { - return name_; - } - - void setName(const std::string& name) { - name_ = name; - } - -private: - QString displayName_; - std::string name_; -}; - -} // namespace Fm - -#endif // FM_PATHBAR_P_H diff --git a/src/pathedit.cpp b/src/pathedit.cpp deleted file mode 100644 index 61da0d0..0000000 --- a/src/pathedit.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 "pathedit.h" -#include "pathedit_p.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Fm { - -void PathEditJob::runJob() { - GError* err = nullptr; - GFileEnumerator* enu = g_file_enumerate_children(dirName, - // G_FILE_ATTRIBUTE_STANDARD_NAME"," - G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME"," - G_FILE_ATTRIBUTE_STANDARD_TYPE, - G_FILE_QUERY_INFO_NONE, cancellable, - &err); - if(enu) { - while(!g_cancellable_is_cancelled(cancellable)) { - GFileInfo* inf = g_file_enumerator_next_file(enu, cancellable, &err); - if(inf) { - GFileType type = g_file_info_get_file_type(inf); - if(type == G_FILE_TYPE_DIRECTORY) { - const char* name = g_file_info_get_display_name(inf); - // FIXME: encoding conversion here? - subDirs.append(QString::fromUtf8(name)); - } - g_object_unref(inf); - } - else { - if(err) { - g_error_free(err); - err = nullptr; - } - else { /* EOF */ - break; - } - } - } - g_file_enumerator_close(enu, cancellable, nullptr); - g_object_unref(enu); - } - // finished! let's update the UI in the main thread - Q_EMIT finished(); - QThread::currentThread()->quit(); -} - - -PathEdit::PathEdit(QWidget* parent): - QLineEdit(parent), - completer_(new QCompleter()), - model_(new QStringListModel()), - cancellable_(nullptr) { - setCompleter(completer_); - completer_->setModel(model_); - connect(this, &PathEdit::textChanged, this, &PathEdit::onTextChanged); - connect(this, &PathEdit::textEdited, this, &PathEdit::onTextEdited); -} - -PathEdit::~PathEdit() { - delete completer_; - if(model_) { - delete model_; - } - if(cancellable_) { - g_cancellable_cancel(cancellable_); - g_object_unref(cancellable_); - } -} - -void PathEdit::focusInEvent(QFocusEvent* e) { - QLineEdit::focusInEvent(e); - // build the completion list only when we have the keyboard focus - reloadCompleter(true); -} - -void PathEdit::focusOutEvent(QFocusEvent* e) { - QLineEdit::focusOutEvent(e); - // free the completion list since we don't need it anymore - freeCompleter(); -} - -bool PathEdit::event(QEvent* e) { - // Stop Qt from moving the keyboard focus to the next widget when "Tab" is pressed. - // Instead, we need to do auto-completion in this case. - if(e->type() == QEvent::KeyPress) { - QKeyEvent* keyEvent = static_cast(e); - if(keyEvent->key() == Qt::Key_Tab && keyEvent->modifiers() == Qt::NoModifier) { // Tab key is pressed - e->accept(); - // do auto-completion when the user press the Tab key. - // This fixes #201: https://github.com/lxqt/pcmanfm-qt/issues/201 - autoComplete(); - return true; - } - } - return QLineEdit::event(e); -} - -void PathEdit::onTextEdited(const QString& text) { - // just replace start tilde with home path if text is changed by user - if(text == QLatin1String("~") || text.startsWith(QLatin1String("~/"))) { - QString txt(text); - txt.replace(0, 1, QDir::homePath()); - setText(txt); // emits textChanged() - return; - } -} - -void PathEdit::onTextChanged(const QString& text) { - if(text == QLatin1String("~") || text.startsWith(QLatin1String("~/"))) { - // do nothing with a start tilde because neither Fm::FilePath nor autocompletion - // understands it; instead, wait until textChanged() is emitted again without it - // WARNING: replacing tilde may not be safe here - return; - } - int pos = text.lastIndexOf('/'); - if(pos >= 0) { - ++pos; - } - else { - pos = text.length(); - } - QString newPrefix = text.left(pos); - if(currentPrefix_ != newPrefix) { - currentPrefix_ = newPrefix; - // only build the completion list if we have the keyboard focus - // if we don't have the focus now, then we'll rebuild the completion list - // when focusInEvent happens. this avoid unnecessary dir loading. - if(hasFocus()) { - reloadCompleter(false); - } - } -} - -void PathEdit::autoComplete() { - // find longest common prefix of the strings currently shown in the candidate list - QAbstractItemModel* model = completer_->completionModel(); - if(model->rowCount() > 0) { - int minLen = text().length(); - QString commonPrefix = model->data(model->index(0, 0)).toString(); - for(int row = 1; row < model->rowCount() && commonPrefix.length() > minLen; ++row) { - QModelIndex index = model->index(row, 0); - QString rowText = model->data(index).toString(); - int prefixLen = 0; - while(prefixLen < rowText.length() && prefixLen < commonPrefix.length() && rowText[prefixLen] == commonPrefix[prefixLen]) { - ++prefixLen; - } - commonPrefix.truncate(prefixLen); - } - if(commonPrefix.length() > minLen) { - setText(commonPrefix); - } - } -} - -void PathEdit::reloadCompleter(bool triggeredByFocusInEvent) { - // parent dir has been changed, reload dir list - // if(currentPrefix_[0] == "~") { // special case for home dir - // cancel running dir-listing jobs, if there's any - if(cancellable_) { - g_cancellable_cancel(cancellable_); - g_object_unref(cancellable_); - } - - // create a new job to do dir listing - PathEditJob* job = new PathEditJob(); - job->edit = this; - job->triggeredByFocusInEvent = triggeredByFocusInEvent; - // need to use fm_file_new_for_commandline_arg() rather than g_file_new_for_commandline_arg(). - // otherwise, our own vfs, such as menu://, won't be loaded. - job->dirName = fm_file_new_for_commandline_arg(currentPrefix_.toLocal8Bit().constData()); - // qDebug("load: %s", g_file_get_uri(data->dirName)); - cancellable_ = g_cancellable_new(); - job->cancellable = (GCancellable*)g_object_ref(cancellable_); - - // launch a new worker thread to handle the job - QThread* thread = new QThread(); - job->moveToThread(thread); - connect(job, &PathEditJob::finished, this, &PathEdit::onJobFinished, Qt::BlockingQueuedConnection); - // connect(job, &PathEditJob::finished, thread, &QThread::quit); - connect(thread, &QThread::started, job, &PathEditJob::runJob); - connect(thread, &QThread::finished, thread, &QObject::deleteLater); - connect(thread, &QThread::finished, job, &QObject::deleteLater); - thread->start(QThread::LowPriority); -} - -void PathEdit::freeCompleter() { - if(cancellable_) { - g_cancellable_cancel(cancellable_); - g_object_unref(cancellable_); - cancellable_ = nullptr; - } - model_->setStringList(QStringList()); -} - -// This slot is called from main thread so it's safe to access the GUI -void PathEdit::onJobFinished() { - PathEditJob* data = static_cast(sender()); - if(!g_cancellable_is_cancelled(data->cancellable)) { - // update the completer only if the job is not cancelled - QStringList::iterator it; - for(it = data->subDirs.begin(); it != data->subDirs.end(); ++it) { - // qDebug("%s", it->toUtf8().constData()); - *it = (currentPrefix_ % *it); - } - model_->setStringList(data->subDirs); - // trigger completion manually - if(hasFocus() && !data->triggeredByFocusInEvent) { - completer_->complete(); - } - } - else { - model_->setStringList(QStringList()); - } - if(cancellable_) { - g_object_unref(cancellable_); - cancellable_ = nullptr; - } -} - -} // namespace Fm diff --git a/src/pathedit.h b/src/pathedit.h deleted file mode 100644 index 6385ddf..0000000 --- a/src/pathedit.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_PATHEDIT_H -#define FM_PATHEDIT_H - -#include "libfmqtglobals.h" -#include -#include - -class QCompleter; -class QStringListModel; - -namespace Fm { - -class PathEditJob; - -class LIBFM_QT_API PathEdit : public QLineEdit { - Q_OBJECT -public: - explicit PathEdit(QWidget* parent = 0); - virtual ~PathEdit(); - -protected: - virtual void focusInEvent(QFocusEvent* e); - virtual void focusOutEvent(QFocusEvent* e); - virtual bool event(QEvent* e); - -private Q_SLOTS: - void onTextChanged(const QString& text); - void onTextEdited(const QString& text); - -private: - void autoComplete(); - void reloadCompleter(bool triggeredByFocusInEvent = false); - void freeCompleter(); - void onJobFinished(); - -private: - QCompleter* completer_; - QStringListModel* model_; - QString currentPrefix_; - GCancellable* cancellable_; -}; - -} - -#endif // FM_PATHEDIT_H diff --git a/src/pathedit_p.h b/src/pathedit_p.h deleted file mode 100644 index ce0e8f7..0000000 --- a/src/pathedit_p.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_PATHEDIT_P_H -#define FM_PATHEDIT_P_H - -#include -#include - -namespace Fm { - -class PathEdit; - -class PathEditJob : public QObject { - Q_OBJECT -public: - GCancellable* cancellable; - GFile* dirName; - QStringList subDirs; - PathEdit* edit; - bool triggeredByFocusInEvent; - - ~PathEditJob() { - g_object_unref(dirName); - g_object_unref(cancellable); - } - -Q_SIGNALS: - void finished(); - -public Q_SLOTS: - void runJob(); - -}; - -} - -#endif // FM_PATHEDIT_P_H diff --git a/src/placesmodel.cpp b/src/placesmodel.cpp deleted file mode 100644 index 078bbca..0000000 --- a/src/placesmodel.cpp +++ /dev/null @@ -1,614 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 "placesmodel.h" -#include -#include -#include -#include -#include -#include -#include "utilities.h" -#include "placesmodelitem.h" - -namespace Fm { - -std::weak_ptr PlacesModel::globalInstance_; - -PlacesModel::PlacesModel(QObject* parent): - QStandardItemModel(parent), - showApplications_(true), - showDesktop_(true), - // FIXME: this seems to be broken when porting to new API. - ejectIcon_(QIcon::fromTheme("media-eject")) { - setColumnCount(2); - - placesRoot = new QStandardItem(tr("Places")); - placesRoot->setSelectable(false); - placesRoot->setColumnCount(2); - appendRow(placesRoot); - - homeItem = new PlacesModelItem("user-home", g_get_user_name(), Fm::FilePath::homeDir()); - placesRoot->appendRow(homeItem); - - desktopItem = new PlacesModelItem("user-desktop", tr("Desktop"), - Fm::FilePath::fromLocalPath(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).toLocal8Bit().constData())); - placesRoot->appendRow(desktopItem); - - createTrashItem(); - - computerItem = new PlacesModelItem("computer", tr("Computer"), Fm::FilePath::fromUri("computer:///")); - placesRoot->appendRow(computerItem); - - { // Applications - const char* applicaion_icon_names[] = {"system-software-install", "applications-accessories", "application-x-executable"}; - // NOTE: g_themed_icon_new_from_names() accepts char**, but actually const char** is OK. - Fm::GIconPtr gicon{g_themed_icon_new_from_names((char**)applicaion_icon_names, G_N_ELEMENTS(applicaion_icon_names)), false}; - auto fmicon = Fm::IconInfo::fromGIcon(std::move(gicon)); - applicationsItem = new PlacesModelItem(fmicon, tr("Applications"), Fm::FilePath::fromUri("menu:///applications/")); - placesRoot->appendRow(applicationsItem); - } - - { // Network - const char* network_icon_names[] = {"network", "folder-network", "folder"}; - // NOTE: g_themed_icon_new_from_names() accepts char**, but actually const char** is OK. - Fm::GIconPtr gicon{g_themed_icon_new_from_names((char**)network_icon_names, G_N_ELEMENTS(network_icon_names)), false}; - auto fmicon = Fm::IconInfo::fromGIcon(std::move(gicon)); - networkItem = new PlacesModelItem(fmicon, tr("Network"), Fm::FilePath::fromUri("network:///")); - placesRoot->appendRow(networkItem); - } - - devicesRoot = new QStandardItem(tr("Devices")); - devicesRoot->setSelectable(false); - devicesRoot->setColumnCount(2); - appendRow(devicesRoot); - - // volumes - volumeMonitor = g_volume_monitor_get(); - if(volumeMonitor) { - g_signal_connect(volumeMonitor, "volume-added", G_CALLBACK(onVolumeAdded), this); - g_signal_connect(volumeMonitor, "volume-removed", G_CALLBACK(onVolumeRemoved), this); - g_signal_connect(volumeMonitor, "volume-changed", G_CALLBACK(onVolumeChanged), this); - g_signal_connect(volumeMonitor, "mount-added", G_CALLBACK(onMountAdded), this); - g_signal_connect(volumeMonitor, "mount-changed", G_CALLBACK(onMountChanged), this); - g_signal_connect(volumeMonitor, "mount-removed", G_CALLBACK(onMountRemoved), this); - - // add volumes to side-pane - GList* vols = g_volume_monitor_get_volumes(volumeMonitor); - GList* l; - for(l = vols; l; l = l->next) { - GVolume* volume = G_VOLUME(l->data); - onVolumeAdded(volumeMonitor, volume, this); - g_object_unref(volume); - } - g_list_free(vols); - - /* add mounts to side-pane */ - vols = g_volume_monitor_get_mounts(volumeMonitor); - for(l = vols; l; l = l->next) { - GMount* mount = G_MOUNT(l->data); - GVolume* volume = g_mount_get_volume(mount); - if(volume) { - g_object_unref(volume); - } - else { /* network mounts or others */ - gboolean shadowed = FALSE; -#if GLIB_CHECK_VERSION(2, 20, 0) - shadowed = g_mount_is_shadowed(mount); -#endif - // according to gio API doc, a shadowed mount should not be visible to the user - if(shadowed) { - shadowedMounts_.push_back(mount); - continue; - } - else { - PlacesModelItem* item = new PlacesModelMountItem(mount); - devicesRoot->appendRow(item); - } - } - g_object_unref(mount); - } - g_list_free(vols); - } - - // bookmarks - bookmarksRoot = new QStandardItem(tr("Bookmarks")); - bookmarksRoot->setSelectable(false); - bookmarksRoot->setColumnCount(2); - appendRow(bookmarksRoot); - - bookmarks = Fm::Bookmarks::globalInstance(); - loadBookmarks(); - connect(bookmarks.get(), &Fm::Bookmarks::changed, this, &PlacesModel::onBookmarksChanged); -} - -void PlacesModel::loadBookmarks() { - for(auto& bm_item: bookmarks->items()) { - PlacesModelBookmarkItem* item = new PlacesModelBookmarkItem(bm_item); - bookmarksRoot->appendRow(item); - } -} - -PlacesModel::~PlacesModel() { - if(volumeMonitor) { - g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onVolumeAdded), this); - g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onVolumeRemoved), this); - g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onVolumeChanged), this); - g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onMountAdded), this); - g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onMountChanged), this); - g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onMountRemoved), this); - g_object_unref(volumeMonitor); - } - if(trashMonitor_) { - g_signal_handlers_disconnect_by_func(trashMonitor_, (gpointer)G_CALLBACK(onTrashChanged), this); - g_object_unref(trashMonitor_); - } - - for(GMount* const mount : qAsConst(shadowedMounts_)) { - g_object_unref(mount); - } -} - -// static -void PlacesModel::onTrashChanged(GFileMonitor* /*monitor*/, GFile* /*gf*/, GFile* /*other*/, GFileMonitorEvent /*evt*/, PlacesModel* pThis) { - QTimer::singleShot(0, pThis, SLOT(updateTrash())); -} - -void PlacesModel::updateTrash() { - - struct UpdateTrashData { - QPointer model; - GFile* gf; - UpdateTrashData(PlacesModel* _model) : model(_model) { - gf = fm_file_new_for_uri("trash:///"); - } - ~UpdateTrashData() { - g_object_unref(gf); - } - }; - - if(trashItem_) { - UpdateTrashData* data = new UpdateTrashData(this); - g_file_query_info_async(data->gf, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT, G_FILE_QUERY_INFO_NONE, G_PRIORITY_LOW, nullptr, - [](GObject * /*source_object*/, GAsyncResult * res, gpointer user_data) { - // the callback lambda function is called when the asyn query operation is finished - UpdateTrashData* data = reinterpret_cast(user_data); - PlacesModel* _this = data->model.data(); - if(_this != nullptr) { // ensure that our model object is not deleted yet - Fm::GFileInfoPtr inf{g_file_query_info_finish(data->gf, res, nullptr), false}; - if(inf) { - if(_this->trashItem_ != nullptr) { // it's possible that when we finish, the trash item is removed - guint32 n = g_file_info_get_attribute_uint32(inf.get(), G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT); - const char* icon_name = n > 0 ? "user-trash-full" : "user-trash"; - auto icon = Fm::IconInfo::fromName(icon_name); - _this->trashItem_->setIcon(std::move(icon)); - } - } - } - delete data; // free the data used for this async operation. - }, data); - } -} - -void PlacesModel::createTrashItem() { - GFile* gf; - gf = fm_file_new_for_uri("trash:///"); - // check if trash is supported by the current vfs - // if gvfs is not installed, this can be unavailable. - if(!g_file_query_exists(gf, nullptr)) { - g_object_unref(gf); - trashItem_ = nullptr; - trashMonitor_ = nullptr; - return; - } - trashItem_ = new PlacesModelItem("user-trash", tr("Trash"), Fm::FilePath::fromUri("trash:///")); - - trashMonitor_ = fm_monitor_directory(gf, nullptr); - if(trashMonitor_) { - g_signal_connect(trashMonitor_, "changed", G_CALLBACK(onTrashChanged), this); - } - g_object_unref(gf); - - placesRoot->insertRow(desktopItem->row() + 1, trashItem_); - QTimer::singleShot(0, this, SLOT(updateTrash())); -} - -void PlacesModel::setShowApplications(bool show) { - if(showApplications_ != show) { - showApplications_ = show; - } -} - -void PlacesModel::setShowDesktop(bool show) { - if(showDesktop_ != show) { - showDesktop_ = show; - } -} - -void PlacesModel::setShowTrash(bool show) { - if(show) { - if(!trashItem_) { - createTrashItem(); - } - } - else { - if(trashItem_) { - if(trashMonitor_) { - g_signal_handlers_disconnect_by_func(trashMonitor_, (gpointer)G_CALLBACK(onTrashChanged), this); - g_object_unref(trashMonitor_); - trashMonitor_ = nullptr; - } - placesRoot->removeRow(trashItem_->row()); // delete trashItem_; - trashItem_ = nullptr; - } - } -} - -PlacesModelItem* PlacesModel::itemFromPath(const Fm::FilePath &path) { - PlacesModelItem* item = itemFromPath(placesRoot, path); - if(!item) { - item = itemFromPath(devicesRoot, path); - } - if(!item) { - item = itemFromPath(bookmarksRoot, path); - } - return item; -} - -PlacesModelItem* PlacesModel::itemFromPath(QStandardItem* rootItem, const Fm::FilePath &path) { - int rowCount = rootItem->rowCount(); - for(int i = 0; i < rowCount; ++i) { - PlacesModelItem* item = static_cast(rootItem->child(i, 0)); - if(item->path() == path) { - return item; - } - } - return nullptr; -} - -PlacesModelVolumeItem* PlacesModel::itemFromVolume(GVolume* volume) { - int rowCount = devicesRoot->rowCount(); - for(int i = 0; i < rowCount; ++i) { - PlacesModelItem* item = static_cast(devicesRoot->child(i, 0)); - if(item->type() == PlacesModelItem::Volume) { - PlacesModelVolumeItem* volumeItem = static_cast(item); - if(volumeItem->volume() == volume) { - return volumeItem; - } - } - } - return nullptr; -} - -PlacesModelMountItem* PlacesModel::itemFromMount(GMount* mount) { - int rowCount = devicesRoot->rowCount(); - for(int i = 0; i < rowCount; ++i) { - PlacesModelItem* item = static_cast(devicesRoot->child(i, 0)); - if(item->type() == PlacesModelItem::Mount) { - PlacesModelMountItem* mountItem = static_cast(item); - if(mountItem->mount() == mount) { - return mountItem; - } - } - } - return nullptr; -} - -PlacesModelBookmarkItem* PlacesModel::itemFromBookmark(std::shared_ptr bkitem) { - int rowCount = bookmarksRoot->rowCount(); - for(int i = 0; i < rowCount; ++i) { - PlacesModelBookmarkItem* item = static_cast(bookmarksRoot->child(i, 0)); - if(item->bookmark() == bkitem) { - return item; - } - } - return nullptr; -} - -void PlacesModel::onMountAdded(GVolumeMonitor* /*monitor*/, GMount* mount, PlacesModel* pThis) { - // according to gio API doc, a shadowed mount should not be visible to the user -#if GLIB_CHECK_VERSION(2, 20, 0) - if(g_mount_is_shadowed(mount)) { - if(pThis->shadowedMounts_.indexOf(mount) == -1) { - pThis->shadowedMounts_.push_back(G_MOUNT(g_object_ref(mount))); - } - return; - } -#endif - GVolume* vol = g_mount_get_volume(mount); - if(vol) { // mount-added is also emitted when a volume is newly mounted. - PlacesModelVolumeItem* item = pThis->itemFromVolume(vol); - if(item && !item->path()) { - // update the mounted volume and show a button for eject. - Fm::FilePath path{g_mount_get_root(mount), false}; - item->setPath(path); - // update the mount indicator (eject button) - QStandardItem* ejectBtn = item->parent()->child(item->row(), 1); - Q_ASSERT(ejectBtn); - ejectBtn->setIcon(pThis->ejectIcon_); - } - g_object_unref(vol); - } - else { // network mounts and others - PlacesModelMountItem* item = pThis->itemFromMount(mount); - /* for some unknown reasons, sometimes we get repeated mount-added - * signals and added a device more than one. So, make a sanity check here. */ - if(!item) { - item = new PlacesModelMountItem(mount); - QStandardItem* eject_btn = new QStandardItem(pThis->ejectIcon_, QString()); - pThis->devicesRoot->appendRow(QList() << item << eject_btn); - } - } -} - -void PlacesModel::onMountChanged(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis) { - gboolean shadowed = FALSE; - // according to gio API doc, a shadowed mount should not be visible to the user -#if GLIB_CHECK_VERSION(2, 20, 0) - shadowed = g_mount_is_shadowed(mount); - // qDebug() << "changed:" << mount << shadowed; -#endif - PlacesModelMountItem* item = pThis->itemFromMount(mount); - if(item) { - if(shadowed) { // if a visible item becomes shadowed, remove it from the model - pThis->shadowedMounts_.push_back(G_MOUNT(g_object_ref(mount))); // remember the shadowed mount - pThis->devicesRoot->removeRow(item->row()); - } - else { // otherwise, update its status - item->update(); - } - } - else { -#if GLIB_CHECK_VERSION(2, 20, 0) - if(!shadowed) { // if a mount is unshadowed - int i = pThis->shadowedMounts_.indexOf(mount); - if(i != -1) { // a previously shadowed mount is unshadowed - pThis->shadowedMounts_.removeAt(i); - onMountAdded(monitor, mount, pThis); // add it to our model again - } - } -#endif - } -} - -void PlacesModel::onMountRemoved(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis) { - GVolume* vol = g_mount_get_volume(mount); - // qDebug() << "mount removed" << mount << "volume umounted: " << vol; - if(vol) { - // a volume is being unmounted - // NOTE: Due to some problems of gvfs, sometimes the volume does not receive - // "change" signal so it does not update the eject button. Let's workaround - // this by calling onVolumeChanged() handler manually. (This is needed for mtp://) - onVolumeChanged(monitor, vol, pThis); - g_object_unref(vol); - } - else { // network mounts and others - PlacesModelMountItem* item = pThis->itemFromMount(mount); - if(item) { - pThis->devicesRoot->removeRow(item->row()); - } - } - -#if GLIB_CHECK_VERSION(2, 20, 0) - // NOTE: g_mount_is_shadowed() sometimes returns FALSE here even if the mount is shadowed. - // I don't know whether this is a bug in gvfs or not. - // So let's check if its in our list instead. - if(pThis->shadowedMounts_.removeOne(mount)) { - // if this is a shadowed mount - // qDebug() << "remove shadow mount"; - g_object_unref(mount); - } -#endif -} - -void PlacesModel::onVolumeAdded(GVolumeMonitor* /*monitor*/, GVolume* volume, PlacesModel* pThis) { - // the item may have been added with "mount-added" (as in loopback mounting) - bool itemExists = false; - GMount* mount = g_volume_get_mount(volume); - if(mount) { - if(pThis->itemFromMount(mount)) { - itemExists = true; - } - g_object_unref(mount); - } - if(itemExists) { - return; - } - // for some unknown reasons, sometimes we get repeated volume-added - // signals and added a device more than one. So, make a sanity check here. - PlacesModelVolumeItem* volumeItem = pThis->itemFromVolume(volume); - if(!volumeItem) { - volumeItem = new PlacesModelVolumeItem(volume); - QStandardItem* ejectBtn = new QStandardItem(); - if(volumeItem->isMounted()) { - ejectBtn->setIcon(pThis->ejectIcon_); - } - pThis->devicesRoot->appendRow(QList() << volumeItem << ejectBtn); - } -} - -void PlacesModel::onVolumeChanged(GVolumeMonitor* /*monitor*/, GVolume* volume, PlacesModel* pThis) { - PlacesModelVolumeItem* item = pThis->itemFromVolume(volume); - if(item) { - item->update(); - if(!item->isMounted()) { // the volume is unmounted, remove the eject button if needed - // remove the eject button for the volume (at column 1 of the same row) - QStandardItem* ejectBtn = item->parent()->child(item->row(), 1); - Q_ASSERT(ejectBtn); - ejectBtn->setIcon(QIcon()); - } - } -} - -void PlacesModel::onVolumeRemoved(GVolumeMonitor* /*monitor*/, GVolume* volume, PlacesModel* pThis) { - PlacesModelVolumeItem* item = pThis->itemFromVolume(volume); - if(item) { - pThis->devicesRoot->removeRow(item->row()); - } -} - -void PlacesModel::onBookmarksChanged() { - // remove all items - bookmarksRoot->removeRows(0, bookmarksRoot->rowCount()); - loadBookmarks(); -} - -Qt::ItemFlags PlacesModel::flags(const QModelIndex& index) const { - if(index.column() == 1) { // make 2nd column of every row selectable. - return Qt::ItemIsSelectable | Qt::ItemIsEnabled; - } - if(!index.parent().isValid()) { // root items - if(index.row() == 2) { // bookmarks root - return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; - } - else { - return Qt::ItemIsEnabled; - } - } - return QStandardItemModel::flags(index); -} - - -QVariant PlacesModel::data(const QModelIndex& index, int role) const { - if(index.column() == 0 && index.parent().isValid()) { - PlacesModelItem* item = static_cast(QStandardItemModel::itemFromIndex(index)); - if(item != nullptr) { - switch(role) { - case FileInfoRole: - return QVariant::fromValue(item->fileInfo()); - case FmIconRole: - return QVariant::fromValue(item->icon()); - } - } - } - return QStandardItemModel::data(index, role); -} - -std::shared_ptr PlacesModel::globalInstance() { - auto model = globalInstance_.lock(); - if(!model) { - model = std::make_shared(); - globalInstance_ = model; - } - return model; -} - - -bool PlacesModel::dropMimeData(const QMimeData* data, Qt::DropAction /*action*/, int row, int column, const QModelIndex& parent) { - QStandardItem* item = itemFromIndex(parent); - if(data->hasFormat("application/x-bookmark-row")) { // the data being dopped is a bookmark row - // decode it and do bookmark reordering - QByteArray buf = data->data("application/x-bookmark-row"); - QDataStream stream(&buf, QIODevice::ReadOnly); - int oldPos = -1; - char* pathStr = nullptr; - stream >> oldPos >> pathStr; - // find the source bookmark item being dragged - auto allBookmarks = bookmarks->items(); - auto& draggedItem = allBookmarks[oldPos]; - // If we cannot find the dragged bookmark item at position , or we find an item, - // but the path of the item is not the same as what we expected, than it's the wrong item. - // This means that the bookmarks are changed during our dnd processing, which is an extremely rare case. - auto draggedPath = Fm::FilePath::fromPathStr(pathStr); - if(!draggedItem || draggedItem->path() != draggedPath) { - delete []pathStr; - return false; - } - delete []pathStr; - - int newPos = -1; - if(row == -1 && column == -1) { // drop on an item - // we only allow dropping on an bookmark item - if(item && item->parent() == bookmarksRoot) { - newPos = parent.row(); - } - } - else { // drop on a position between items - if(item == bookmarksRoot) { // we only allow dropping on a bookmark item - newPos = row; - } - } - if(newPos != -1 && newPos != oldPos) { // reorder the bookmark item - bookmarks->reorder(draggedItem, newPos); - } - } - else if(data->hasUrls()) { // files uris are dropped - if(row == -1 && column == -1) { // drop uris on an item - if(item && item->parent()) { // need to be a child item - PlacesModelItem* placesItem = static_cast(item); - if(placesItem->path()) { - qDebug() << "dropped dest:" << placesItem->text(); - // TODO: copy or move the dragged files to the dir pointed by the item. - qDebug() << "drop on" << item->text(); - } - } - } - else { // drop uris on a position between items - if(item == bookmarksRoot) { // we only allow dropping on blank row of bookmarks section - auto paths = pathListFromQUrls(data->urls()); - for(auto& path: paths) { - // FIXME: this is a blocking call - if(g_file_query_file_type(path.gfile().get(), G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - nullptr) == G_FILE_TYPE_DIRECTORY) { - auto disp_name = path.baseName(); - bookmarks->insert(path, disp_name.get(), row); - } - return true; - } - } - } - } - return false; -} - -// we only support dragging bookmark items and use our own -// custom pseudo-mime-type: application/x-bookmark-row -QMimeData* PlacesModel::mimeData(const QModelIndexList& indexes) const { - if(!indexes.isEmpty()) { - // we only allow dragging one bookmark item at a time, so handle the first index only. - QModelIndex index = indexes.first(); - QStandardItem* item = itemFromIndex(index); - // ensure that it's really a bookmark item - if(item && item->parent() == bookmarksRoot) { - PlacesModelBookmarkItem* bookmarkItem = static_cast(item); - QMimeData* mime = new QMimeData(); - QByteArray data; - QDataStream stream(&data, QIODevice::WriteOnly); - // There is no safe and cross-process way to store a reference of a row. - // Let's store the pos, name, and path of the bookmark item instead. - auto pathStr = bookmarkItem->path().toString(); - stream << index.row() << pathStr.get(); - mime->setData("application/x-bookmark-row", data); - return mime; - } - } - return nullptr; -} - -QStringList PlacesModel::mimeTypes() const { - return QStringList() << "application/x-bookmark-row" << "text/uri-list"; -} - -Qt::DropActions PlacesModel::supportedDropActions() const { - return QStandardItemModel::supportedDropActions(); -} - - -} // namespace Fm diff --git a/src/placesmodel.h b/src/placesmodel.h deleted file mode 100644 index 08d0a5a..0000000 --- a/src/placesmodel.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_PLACESMODEL_H -#define FM_PLACESMODEL_H - -#include "libfmqtglobals.h" -#include -#include -#include -#include -#include - -#include - -#include "core/filepath.h" -#include "core/bookmarks.h" - -namespace Fm { - -class PlacesModelItem; -class PlacesModelVolumeItem; -class PlacesModelMountItem; -class PlacesModelBookmarkItem; - -class LIBFM_QT_API PlacesModel : public QStandardItemModel { - Q_OBJECT - friend class PlacesView; -public: - - enum { - FileInfoRole = Qt::UserRole, - FmIconRole - }; - - // QAction used for popup menus - class ItemAction : public QAction { - public: - explicit ItemAction(const QModelIndex& index, QString text, QObject* parent = 0): - QAction(text, parent), - index_(index) { - } - - QPersistentModelIndex& index() { - return index_; - } - private: - QPersistentModelIndex index_; - }; - -public: - explicit PlacesModel(QObject* parent = 0); - virtual ~PlacesModel(); - - bool showTrash() { - return trashItem_ != nullptr; - } - void setShowTrash(bool show); - - bool showApplications() { - return showApplications_; - } - void setShowApplications(bool show); - - bool showDesktop() { - return showDesktop_; - } - void setShowDesktop(bool show); - - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; - - static std::shared_ptr globalInstance(); - -public Q_SLOTS: - void updateTrash(); - void onBookmarksChanged(); - -protected: - - PlacesModelItem* itemFromPath(const Fm::FilePath& path); - PlacesModelItem* itemFromPath(QStandardItem* rootItem, const Fm::FilePath & path); - PlacesModelVolumeItem* itemFromVolume(GVolume* volume); - PlacesModelMountItem* itemFromMount(GMount* mount); - PlacesModelBookmarkItem* itemFromBookmark(std::shared_ptr bkitem); - - virtual Qt::ItemFlags flags(const QModelIndex& index) const; - virtual QStringList mimeTypes() const; - virtual QMimeData* mimeData(const QModelIndexList& indexes) const; - virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); - Qt::DropActions supportedDropActions() const; - - void createTrashItem(); - -private: - void loadBookmarks(); - - static void onVolumeAdded(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis); - static void onVolumeRemoved(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis); - static void onVolumeChanged(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis); - static void onMountAdded(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis); - static void onMountRemoved(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis); - static void onMountChanged(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis); - - static void onTrashChanged(GFileMonitor* monitor, GFile* gf, GFile* other, GFileMonitorEvent evt, PlacesModel* pThis); - -private: - std::shared_ptr bookmarks; - GVolumeMonitor* volumeMonitor; - QList jobs; - bool showApplications_; - bool showDesktop_; - QStandardItem* placesRoot; - QStandardItem* devicesRoot; - QStandardItem* bookmarksRoot; - PlacesModelItem* trashItem_; - GFileMonitor* trashMonitor_; - PlacesModelItem* desktopItem; - PlacesModelItem* homeItem; - PlacesModelItem* computerItem; - PlacesModelItem* networkItem; - PlacesModelItem* applicationsItem; - QIcon ejectIcon_; - QList shadowedMounts_; - - static std::weak_ptr globalInstance_; -}; - -} - -#endif // FM_PLACESMODEL_H diff --git a/src/placesmodelitem.cpp b/src/placesmodelitem.cpp deleted file mode 100644 index f204223..0000000 --- a/src/placesmodelitem.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 "placesmodelitem.h" -#include -#include - -namespace Fm { - -PlacesModelItem::PlacesModelItem(): - QStandardItem(), - fileInfo_(nullptr), - icon_(nullptr) { -} - -PlacesModelItem::PlacesModelItem(const char* iconName, QString title, Fm::FilePath path): - QStandardItem(title), - path_{std::move(path)}, - icon_(Fm::IconInfo::fromName(iconName)) { - if(icon_) { - QStandardItem::setIcon(icon_->qicon()); - } - setEditable(false); -} - -PlacesModelItem::PlacesModelItem(std::shared_ptr icon, QString title, Fm::FilePath path): - QStandardItem(title), - path_{std::move(path)}, - icon_{std::move(icon)} { - if(icon_) { - QStandardItem::setIcon(icon_->qicon()); - } - setEditable(false); -} - -PlacesModelItem::PlacesModelItem(QIcon icon, QString title, Fm::FilePath path): - QStandardItem(icon, title), - path_{std::move(path)} { - setEditable(false); -} - -PlacesModelItem::~PlacesModelItem() { -} - - -void PlacesModelItem::setIcon(std::shared_ptr icon) { - icon_= std::move(icon); - if(icon_) { - QStandardItem::setIcon(icon_->qicon()); - } - else { - QStandardItem::setIcon(QIcon()); - } -} - -void PlacesModelItem::setIcon(GIcon* gicon) { - setIcon(Fm::IconInfo::fromGIcon(Fm::GIconPtr{gicon, true})); -} - -void PlacesModelItem::updateIcon() { - if(icon_) { - QStandardItem::setIcon(icon_->qicon()); - } -} - -QVariant PlacesModelItem::data(int role) const { - // we use a QPixmap from FmIcon cache rather than QIcon object for decoration role. - return QStandardItem::data(role); -} - -PlacesModelBookmarkItem::PlacesModelBookmarkItem(std::shared_ptr bm_item): - PlacesModelItem{bm_item->icon(), bm_item->name(), bm_item->path()}, - bookmarkItem_{std::move(bm_item)} { - setEditable(true); -} - -PlacesModelVolumeItem::PlacesModelVolumeItem(GVolume* volume): - PlacesModelItem(), - volume_(reinterpret_cast(g_object_ref(volume))) { - update(); - setEditable(false); -} - -void PlacesModelVolumeItem::update() { - // set title - char* volumeName = g_volume_get_name(volume_); - setText(QString::fromUtf8(volumeName)); - g_free(volumeName); - - // set icon - Fm::GIconPtr gicon{g_volume_get_icon(volume_), false}; - setIcon(gicon.get()); - - // set dir path - Fm::GMountPtr mount{g_volume_get_mount(volume_), false}; - if(mount) { - Fm::FilePath mount_root{g_mount_get_root(mount.get()), false}; - setPath(mount_root); - } - else { - setPath(Fm::FilePath{}); - } -} - - -bool PlacesModelVolumeItem::isMounted() { - GMount* mount = g_volume_get_mount(volume_); - if(mount) { - g_object_unref(mount); - } - return mount != nullptr ? true : false; -} - - -PlacesModelMountItem::PlacesModelMountItem(GMount* mount): - PlacesModelItem(), - mount_(reinterpret_cast(mount)) { - update(); - setEditable(false); -} - -void PlacesModelMountItem::update() { - // set title - setText(QString::fromUtf8(g_mount_get_name(mount_))); - - // set path - Fm::FilePath mount_root{g_mount_get_root(mount_), false}; - setPath(mount_root); - - // set icon - Fm::GIconPtr gicon{g_mount_get_icon(mount_), false}; - setIcon(gicon.get()); -} - -} diff --git a/src/placesmodelitem.h b/src/placesmodelitem.h deleted file mode 100644 index cc5bd57..0000000 --- a/src/placesmodelitem.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_PLACESMODELITEM_H -#define FM_PLACESMODELITEM_H - -#include "libfmqtglobals.h" -#include -#include -#include -#include -#include - -#include "core/fileinfo.h" -#include "core/filepath.h" -#include "core/bookmarks.h" - -namespace Fm { - -// model item -class LIBFM_QT_API PlacesModelItem : public QStandardItem { -public: - enum Type { - Places = QStandardItem::UserType + 1, - Volume, - Mount, - Bookmark - }; - -public: - explicit PlacesModelItem(); - explicit PlacesModelItem(QIcon icon, QString title, Fm::FilePath path = Fm::FilePath{}); - explicit PlacesModelItem(const char* iconName, QString title, Fm::FilePath path = Fm::FilePath{}); - explicit PlacesModelItem(std::shared_ptr icon, QString title, Fm::FilePath path = Fm::FilePath{}); - ~PlacesModelItem(); - - const std::shared_ptr& fileInfo() const { - return fileInfo_; - } - void setFileInfo(std::shared_ptr fileInfo) { - fileInfo_ = std::move(fileInfo); - } - - const Fm::FilePath& path() const { - return path_; - } - void setPath(Fm::FilePath path) { - path_ = std::move(path); - } - - const std::shared_ptr& icon() const { - return icon_; - } - void setIcon(std::shared_ptr icon); - void setIcon(GIcon* gicon); - void updateIcon(); - - QVariant data(int role = Qt::UserRole + 1) const; - - virtual int type() const { - return Places; - } - -private: - Fm::FilePath path_; - std::shared_ptr fileInfo_; - std::shared_ptr icon_; -}; - -class LIBFM_QT_API PlacesModelVolumeItem : public PlacesModelItem { -public: - PlacesModelVolumeItem(GVolume* volume); - bool isMounted(); - bool canEject() { - return g_volume_can_eject(volume_); - } - virtual int type() const { - return Volume; - } - GVolume* volume() { - return volume_; - } - void update(); -private: - GVolume* volume_; -}; - -class LIBFM_QT_API PlacesModelMountItem : public PlacesModelItem { -public: - PlacesModelMountItem(GMount* mount); - virtual int type() const { - return Mount; - } - GMount* mount() const { - return mount_; - } - void update(); -private: - GMount* mount_; -}; - -class LIBFM_QT_API PlacesModelBookmarkItem : public PlacesModelItem { -public: - virtual int type() const { - return Bookmark; - } - PlacesModelBookmarkItem(std::shared_ptr bm_item); - const std::shared_ptr& bookmark() const { - return bookmarkItem_; - } -private: - std::shared_ptr bookmarkItem_; -}; - -} - -#endif // FM_PLACESMODELITEM_H diff --git a/src/placesview.cpp b/src/placesview.cpp deleted file mode 100644 index 6c112ef..0000000 --- a/src/placesview.cpp +++ /dev/null @@ -1,629 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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 "placesview.h" -#include "placesmodel.h" -#include "placesmodelitem.h" -#include "mountoperation.h" -#include "fileoperation.h" -#include -#include -#include -#include -#include -#include "folderitemdelegate.h" - -namespace Fm { - -std::shared_ptr PlacesView::proxyModel_; - -PlacesProxyModel::PlacesProxyModel(QObject* parent) : - QSortFilterProxyModel(parent), - showAll_(false), - hiddenItemsRestored_(false) { -} - -PlacesProxyModel::~PlacesProxyModel() { -} - -void PlacesProxyModel::restoreHiddenItems(const QSet& items) { - // hidden items should be restored only once - if(!hiddenItemsRestored_ && !items.isEmpty()) { - hidden_.clear(); - QSet::const_iterator i = items.constBegin(); - while (i != items.constEnd()) { - if(!(*i).isEmpty()) { - hidden_ << *i; - } - ++i; - } - hiddenItemsRestored_ = true; - invalidateFilter(); - } -} - -void PlacesProxyModel::setHidden(const QString& str, bool hide) { - if(hide) { - if(!str.isEmpty()) { - hidden_ << str; - } - } - else { - hidden_.remove(str); - } - invalidateFilter(); -} - -void PlacesProxyModel::showAll(bool show) { - showAll_ = show; - invalidateFilter(); -} - -bool PlacesProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { - if(showAll_ || hidden_.isEmpty()) { - return true; - } - if(PlacesModel* srcModel = static_cast(sourceModel())) { - QModelIndex index = srcModel->index(source_row, 0, source_parent); - if(PlacesModelItem* item = static_cast(srcModel->itemFromIndex(index))) { - if(item->type() == PlacesModelItem::Places) { - if(auto path = item->path()) { - if(hidden_.contains(path.toString().get())) { - return false; - } - } - } - else if(item->type() == PlacesModelItem::Volume) { - CStrPtr uuid{g_volume_get_uuid(static_cast(item)->volume())}; - if(uuid && hidden_.contains(uuid.get())) { - return false; - } - } - // show a root items only if, at least, one of its children is shown - else if((source_row == 0 || source_row == 1) && !source_parent.isValid()) { - QModelIndex indx = index.child(0, 0); - while(PlacesModelItem* childItem = static_cast(srcModel->itemFromIndex(indx))) { - if(childItem->type() == PlacesModelItem::Places) { - if(auto path = childItem->path()) { - if(!hidden_.contains(path.toString().get())) { - return true; - } - } - } - else if(childItem->type() == PlacesModelItem::Volume) { - CStrPtr uuid{g_volume_get_uuid(static_cast(childItem)->volume())}; - if(uuid == nullptr || !hidden_.contains(uuid.get())) { - return true; - } - } - else { - return true; - } - indx = indx.sibling(indx.row() + 1, 0); - } - return false; - } - } - } - return true; -} - -PlacesView::PlacesView(QWidget* parent): - QTreeView(parent) { - setRootIsDecorated(false); - setHeaderHidden(true); - setIndentation(12); - - /* merge with the surroundings */ - setFrameShape(QFrame::NoFrame); - QPalette p = palette(); - p.setColor(QPalette::Base, QColor(Qt::transparent)); - setPalette(p); - viewport()->setAutoFillBackground(false); - - connect(this, &QTreeView::clicked, this, &PlacesView::onClicked); - connect(this, &QTreeView::pressed, this, &PlacesView::onPressed); - - setIconSize(QSize(24, 24)); - - FolderItemDelegate* delegate = new FolderItemDelegate(this, this); - delegate->setFileInfoRole(PlacesModel::FileInfoRole); - delegate->setIconInfoRole(PlacesModel::FmIconRole); - setItemDelegateForColumn(0, delegate); - - model_ = PlacesModel::globalInstance(); - if(!proxyModel_) { - proxyModel_ = std::make_shared(); - } - if(!proxyModel_->sourceModel()) { // all places-views may have been closed - proxyModel_->setSourceModel(model_.get()); - } - setModel(proxyModel_.get()); - - // these 2 connections are needed to update filtering - connect(model_.get(), &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex&, int, int) { - proxyModel_->setHidden(QString()); // just invalidates filter - expandAll(); - // for some reason (a Qt bug?), spanning is reset - setFirstColumnSpanned(0, QModelIndex(), true); - setFirstColumnSpanned(1, QModelIndex(), true); - setFirstColumnSpanned(2, QModelIndex(), true); - - }); - connect(model_.get(), &QAbstractItemModel::rowsRemoved, this, [this](const QModelIndex&, int, int) { - proxyModel_->setHidden(QString()); - }); - - QHeaderView* headerView = header(); - headerView->setSectionResizeMode(0, QHeaderView::Stretch); - headerView->setSectionResizeMode(1, QHeaderView::Fixed); - headerView->setStretchLastSection(false); - expandAll(); - - // FIXME: is there any better way to make the first column span the whole row? - setFirstColumnSpanned(0, QModelIndex(), true); // places root - setFirstColumnSpanned(1, QModelIndex(), true); // devices root - setFirstColumnSpanned(2, QModelIndex(), true); // bookmarks root - - // the 2nd column is for the eject buttons - setSelectionBehavior(QAbstractItemView::SelectRows); // FIXME: why this does not work? - setAllColumnsShowFocus(false); - - setAcceptDrops(true); - setDragEnabled(true); - - // update the umount button's column width based on icon size - onIconSizeChanged(iconSize()); -#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) // this signal requires Qt >= 5.5 - connect(this, &QAbstractItemView::iconSizeChanged, this, &PlacesView::onIconSizeChanged); -#endif -} - -PlacesView::~PlacesView() { - // qDebug("delete PlacesView"); -} - -void PlacesView::activateRow(int type, const QModelIndex& index) { - if(!index.parent().isValid()) { // ignore root items - return; - } - PlacesModelItem* item = static_cast(model_->itemFromIndex(proxyModel_->mapToSource(index))); - if(item) { - auto path = item->path(); - if(!path) { - // check if mounting volumes is needed - if(item->type() == PlacesModelItem::Volume) { - PlacesModelVolumeItem* volumeItem = static_cast(item); - if(!volumeItem->isMounted()) { - // Mount the volume - GVolume* volume = volumeItem->volume(); - MountOperation* op = new MountOperation(true, this); - op->mount(volume); - // connect(op, SIGNAL(finished(GError*)), SLOT(onMountOperationFinished(GError*))); - // blocking here until the mount operation is finished? - - // FIXME: update status of the volume after mount is finished!! - if(!op->wait()) { - return; - } - path = item->path(); - } - } - } - if(path) { - Q_EMIT chdirRequested(type, path); - } - } -} - -// mouse button pressed -void PlacesView::onPressed(const QModelIndex& index) { - // if middle button is pressed - if(QGuiApplication::mouseButtons() & Qt::MiddleButton) { - // the real item is at column 0 - activateRow(1, 0 == index.column() ? index : index.sibling(index.row(), 0)); - } -} - -void PlacesView::onIconSizeChanged(const QSize& size) { - setColumnWidth(1, size.width() + 5); -} - -void PlacesView::onEjectButtonClicked(PlacesModelItem* item) { - // The eject button is clicked for a device item (volume or mount) - if(item->type() == PlacesModelItem::Volume) { - PlacesModelVolumeItem* volumeItem = static_cast(item); - MountOperation* op = new MountOperation(true, this); - if(volumeItem->canEject()) { // do eject if applicable - op->eject(volumeItem->volume()); - } - else { // otherwise, do unmount instead - op->unmount(volumeItem->volume()); - } - } - else if(item->type() == PlacesModelItem::Mount) { - PlacesModelMountItem* mountItem = static_cast(item); - MountOperation* op = new MountOperation(true, this); - op->unmount(mountItem->mount()); - } - qDebug("PlacesView::onEjectButtonClicked"); -} - -void PlacesView::onClicked(const QModelIndex& index) { - if(!index.parent().isValid()) { // ignore root items - return; - } - - if(index.column() == 0) { - activateRow(0, index); - } - else if(index.column() == 1) { // column 1 contains eject buttons of the mounted devices - if(index.parent() == proxyModel_->mapFromSource(model_->devicesRoot->index())) { // this is a mounted device - // the eject button is clicked - QModelIndex itemIndex = index.sibling(index.row(), 0); // the real item is at column 0 - PlacesModelItem* item = static_cast(model_->itemFromIndex(proxyModel_->mapToSource(itemIndex))); - if(item) { - // eject the volume or the mount - onEjectButtonClicked(item); - } - } - else { - activateRow(0, index.sibling(index.row(), 0)); - } - } -} - -void PlacesView::setCurrentPath(Fm::FilePath path) { - currentPath_ = std::move(path); - if(currentPath_) { - // TODO: search for item with the path in model_ and select it. - PlacesModelItem* item = model_->itemFromPath(currentPath_); - if(item) { - selectionModel()->select(proxyModel_->mapFromSource(item->index()), QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows); - } - else { - clearSelection(); - } - } - else { - clearSelection(); - } -} - - -void PlacesView::dragMoveEvent(QDragMoveEvent* event) { - QTreeView::dragMoveEvent(event); - /* - QModelIndex index = indexAt(event->pos()); - if(event->isAccepted() && index.isValid() && index.parent() == model_->bookmarksRoot->index()) { - if(dropIndicatorPosition() != OnItem) { - event->setDropAction(Qt::LinkAction); - event->accept(); - } - } - */ -} - -void PlacesView::dropEvent(QDropEvent* event) { - QTreeView::dropEvent(event); -} - -void PlacesView::onEmptyTrash() { - Fm::FilePathList files; - files.push_back(Fm::FilePath::fromUri("trash:///")); - Fm::FileOperation::deleteFiles(std::move(files)); -} - -void PlacesView::onMoveBookmarkUp() { - PlacesModel::ItemAction* action = static_cast(sender()); - if(!action->index().isValid()) { - return; - } - PlacesModelBookmarkItem* item = static_cast(model_->itemFromIndex(action->index())); - - int row = item->row(); - if(row > 0) { - auto bookmarkItem = item->bookmark(); - Fm::Bookmarks::globalInstance()->reorder(bookmarkItem, row - 1); - } -} - -void PlacesView::onMoveBookmarkDown() { - PlacesModel::ItemAction* action = static_cast(sender()); - if(!action->index().isValid()) { - return; - } - PlacesModelBookmarkItem* item = static_cast(model_->itemFromIndex(action->index())); - - int row = item->row(); - if(row < model_->rowCount()) { - auto bookmarkItem = item->bookmark(); - Fm::Bookmarks::globalInstance()->reorder(bookmarkItem, row + 1); - } -} - -void PlacesView::onDeleteBookmark() { - PlacesModel::ItemAction* action = static_cast(sender()); - if(!action->index().isValid()) { - return; - } - PlacesModelBookmarkItem* item = static_cast(model_->itemFromIndex(action->index())); - auto bookmarkItem = item->bookmark(); - Fm::Bookmarks::globalInstance()->remove(bookmarkItem); -} - -// virtual -void PlacesView::commitData(QWidget* editor) { - QTreeView::commitData(editor); - PlacesModelBookmarkItem* item = static_cast(model_->itemFromIndex(proxyModel_->mapToSource(currentIndex()))); - auto bookmarkItem = item->bookmark(); - // rename bookmark - Fm::Bookmarks::globalInstance()->rename(bookmarkItem, item->text()); -} - -void PlacesView::onOpenNewTab() { - PlacesModel::ItemAction* action = static_cast(sender()); - if(!action->index().isValid()) { - return; - } - PlacesModelItem* item = static_cast(model_->itemFromIndex(action->index())); - if(item) { - Q_EMIT chdirRequested(1, item->path()); - } -} - -void PlacesView::onOpenNewWindow() { - PlacesModel::ItemAction* action = static_cast(sender()); - if(!action->index().isValid()) { - return; - } - PlacesModelItem* item = static_cast(model_->itemFromIndex(action->index())); - if(item) { - Q_EMIT chdirRequested(2, item->path()); - } -} - -void PlacesView::onRenameBookmark() { - PlacesModel::ItemAction* action = static_cast(sender()); - if(!action->index().isValid()) { - return; - } - PlacesModelBookmarkItem* item = static_cast(model_->itemFromIndex(action->index())); - setFocus(); - setCurrentIndex(proxyModel_->mapFromSource(item->index())); - edit(proxyModel_->mapFromSource(item->index())); -} - -void PlacesView::onMountVolume() { - PlacesModel::ItemAction* action = static_cast(sender()); - if(!action->index().isValid()) { - return; - } - PlacesModelVolumeItem* item = static_cast(model_->itemFromIndex(action->index())); - MountOperation* op = new MountOperation(true, this); - op->mount(item->volume()); - op->wait(); -} - -void PlacesView::onUnmountVolume() { - PlacesModel::ItemAction* action = static_cast(sender()); - if(!action->index().isValid()) { - return; - } - PlacesModelVolumeItem* item = static_cast(model_->itemFromIndex(action->index())); - MountOperation* op = new MountOperation(true, this); - op->unmount(item->volume()); - op->wait(); -} - -void PlacesView::onUnmountMount() { - PlacesModel::ItemAction* action = static_cast(sender()); - if(!action->index().isValid()) { - return; - } - PlacesModelMountItem* item = static_cast(model_->itemFromIndex(action->index())); - GMount* mount = item->mount(); - MountOperation* op = new MountOperation(true, this); - op->unmount(mount); - op->wait(); -} - -void PlacesView::onEjectVolume() { - PlacesModel::ItemAction* action = static_cast(sender()); - if(!action->index().isValid()) { - return; - } - PlacesModelVolumeItem* item = static_cast(model_->itemFromIndex(action->index())); - MountOperation* op = new MountOperation(true, this); - op->eject(item->volume()); - op->wait(); -} - -void PlacesView::contextMenuEvent(QContextMenuEvent* event) { - QModelIndex index = indexAt(event->pos()); - if(index.isValid()) { - if(index.column() != 0) { // the real item is at column 0 - index = index.sibling(index.row(), 0); - } - - // Do not take the ownership of the menu since - // it will be deleted with deleteLater() upon hidden. - // This is possibly related to #145 - https://github.com/lxqt/pcmanfm-qt/issues/145 - QMenu* menu = new QMenu(); - QAction* action = nullptr; - PlacesModelItem* item = static_cast(model_->itemFromIndex(proxyModel_->mapToSource(index))); - - if(index.parent().isValid() - && item->type() != PlacesModelItem::Mount - && (item->type() != PlacesModelItem::Volume - || static_cast(item)->isMounted())) { - action = new PlacesModel::ItemAction(item->index(), tr("Open in New Tab"), menu); - connect(action, &QAction::triggered, this, &PlacesView::onOpenNewTab); - menu->addAction(action); - action = new PlacesModel::ItemAction(item->index(), tr("Open in New Window"), menu); - connect(action, &QAction::triggered, this, &PlacesView::onOpenNewWindow); - menu->addAction(action); - } - - switch(item->type()) { - case PlacesModelItem::Places: { - auto path = item->path(); - // FIXME: inefficient - if(path) { - auto path_str = path.toString(); - if(strcmp(path_str.get(), "trash:///") == 0) { - action = new PlacesModel::ItemAction(item->index(), tr("Empty Trash"), menu); - auto icn = item->icon(); - if(icn && icn->qicon().name() == QLatin1String("user-trash")) { // surely an empty trash - action->setEnabled(false); - } - else { - connect(action, &QAction::triggered, this, &PlacesView::onEmptyTrash); - } - // add the "Empty Trash" item on the top - QList actions = menu->actions(); - if(!actions.isEmpty()) { - menu->insertAction(actions.at(0), action); - menu->insertSeparator(actions.at(0)); - } - else { // impossible - menu->addAction(action); - } - } - // add a "Hide" action to the end - menu->addSeparator(); - action = new PlacesModel::ItemAction(item->index(), tr("Hide"), menu); - QString pathStr(path_str.get()); - action->setCheckable(true); - if(proxyModel_->isShowingAll()) { - action->setChecked(proxyModel_->isHidden(pathStr)); - } - connect(action, &QAction::triggered, [this, pathStr](bool checked) { - proxyModel_->setHidden(pathStr, checked); - Q_EMIT hiddenItemSet(pathStr, checked); - }); - menu->addAction(action); - } - break; - } - case PlacesModelItem::Bookmark: { - // create context menu for bookmark item - if(item->index().row() > 0) { - action = new PlacesModel::ItemAction(item->index(), tr("Move Bookmark Up"), menu); - connect(action, &QAction::triggered, this, &PlacesView::onMoveBookmarkUp); - menu->addAction(action); - } - if(item->index().row() < model_->rowCount()) { - action = new PlacesModel::ItemAction(item->index(), tr("Move Bookmark Down"), menu); - connect(action, &QAction::triggered, this, &PlacesView::onMoveBookmarkDown); - menu->addAction(action); - } - action = new PlacesModel::ItemAction(item->index(), tr("Rename Bookmark"), menu); - connect(action, &QAction::triggered, this, &PlacesView::onRenameBookmark); - menu->addAction(action); - action = new PlacesModel::ItemAction(item->index(), tr("Remove Bookmark"), menu); - connect(action, &QAction::triggered, this, &PlacesView::onDeleteBookmark); - menu->addAction(action); - break; - } - case PlacesModelItem::Volume: { - PlacesModelVolumeItem* volumeItem = static_cast(item); - - if(volumeItem->isMounted()) { - action = new PlacesModel::ItemAction(item->index(), tr("Unmount"), menu); - connect(action, &QAction::triggered, this, &PlacesView::onUnmountVolume); - } - else { - action = new PlacesModel::ItemAction(item->index(), tr("Mount"), menu); - connect(action, &QAction::triggered, this, &PlacesView::onMountVolume); - } - menu->addAction(action); - - if(volumeItem->canEject()) { - action = new PlacesModel::ItemAction(item->index(), tr("Eject"), menu); - connect(action, &QAction::triggered, this, &PlacesView::onEjectVolume); - menu->addAction(action); - } - // add a "Hide" action to the end - CStrPtr uuid{g_volume_get_uuid(static_cast(item)->volume())}; - if(uuid) { - QString str = uuid.get(); - menu->addSeparator(); - action = new PlacesModel::ItemAction(item->index(), tr("Hide"), menu); - action->setCheckable(true); - if(proxyModel_->isShowingAll()) { - action->setChecked(proxyModel_->isHidden(str)); - } - connect(action, &QAction::triggered, [this, str](bool checked) { - proxyModel_->setHidden(str, checked); - Q_EMIT hiddenItemSet(str, checked); - }); - menu->addAction(action); - } - break; - } - case PlacesModelItem::Mount: { - action = new PlacesModel::ItemAction(item->index(), tr("Unmount"), menu); - connect(action, &QAction::triggered, this, &PlacesView::onUnmountMount); - menu->addAction(action); - break; - } - } - - // also add an acton for showing all hidden items - if(proxyModel_->hasHidden()) { - if(item->type() == PlacesModelItem::Bookmark) { - menu->addSeparator(); - } - action = new PlacesModel::ItemAction(item->index(), tr("Show All Entries"), menu); - action->setCheckable(true); - action->setChecked(proxyModel_->isShowingAll()); - connect(action, &QAction::triggered, [this](bool checked) { - showAll(checked); - }); - menu->addAction(action); - } - - if(menu->actions().size()) { - menu->popup(mapToGlobal(event->pos())); - connect(menu, &QMenu::aboutToHide, menu, &QMenu::deleteLater); - } - else { - menu->deleteLater(); - } - } -} - -void PlacesView::restoreHiddenItems(const QSet& items) { - proxyModel_->restoreHiddenItems(items); -} - -void PlacesView::showAll(bool show) { - proxyModel_->showAll(show); - if(show) { - expandAll(); - // for some reason (a Qt bug?), spanning is reset - setFirstColumnSpanned(0, QModelIndex(), true); - setFirstColumnSpanned(1, QModelIndex(), true); - setFirstColumnSpanned(2, QModelIndex(), true); - } -} - -} // namespace Fm diff --git a/src/placesview.h b/src/placesview.h deleted file mode 100644 index 335c55e..0000000 --- a/src/placesview.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) - * - * 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_PLACESVIEW_H -#define FM_PLACESVIEW_H - -#include "libfmqtglobals.h" -#include -#include -#include - -#include -#include "core/filepath.h" - -namespace Fm { - -class PlacesModel; -class PlacesModelItem; -class PlacesView; - -class LIBFM_QT_API PlacesProxyModel : public QSortFilterProxyModel { - Q_OBJECT -public: - explicit PlacesProxyModel(QObject* parent = 0); - virtual ~PlacesProxyModel(); - - void setHidden(const QString& str, bool hide = true); - - void restoreHiddenItems(const QSet& items); - - void showAll(bool show); - - bool isShowingAll() const { - return showAll_; - } - - bool isHidden(const QString& str) const { - return hidden_.contains(str); - } - - bool hasHidden() const { - return !hidden_.isEmpty(); - } - -protected: - bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const; - -private: - QSet hidden_; - bool showAll_; - bool hiddenItemsRestored_; -}; - -class LIBFM_QT_API PlacesView : public QTreeView { - Q_OBJECT - -public: - explicit PlacesView(QWidget* parent = 0); - virtual ~PlacesView(); - - void setCurrentPath(Fm::FilePath path); - - const Fm::FilePath& currentPath() const { - return currentPath_; - } - - void chdir(Fm::FilePath path) { - setCurrentPath(std::move(path)); - } - -#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) - void setIconSize(const QSize& size) { - // The signal QAbstractItemView::iconSizeChanged is only available after Qt 5.5. - // To simulate the effect for older Qt versions, we override setIconSize(). - QAbstractItemView::setIconSize(size); - onIconSizeChanged(size); - } -#endif - - void restoreHiddenItems(const QSet& items); - -Q_SIGNALS: - void chdirRequested(int type, const Fm::FilePath& path); - void hiddenItemSet(const QString& str, bool hide); - -protected Q_SLOTS: - void onClicked(const QModelIndex& index); - void onPressed(const QModelIndex& index); - void onIconSizeChanged(const QSize& size); - // void onMountOperationFinished(GError* error); - - void onOpenNewTab(); - void onOpenNewWindow(); - - void onEmptyTrash(); - - void onMountVolume(); - void onUnmountVolume(); - void onEjectVolume(); - void onUnmountMount(); - - void onMoveBookmarkUp(); - void onMoveBookmarkDown(); - void onDeleteBookmark(); - void onRenameBookmark(); - -protected: - void drawBranches(QPainter* /*painter*/, const QRect& /*rect*/, const QModelIndex& /*index*/) const { - // override this method to inhibit drawing of the branch grid lines by Qt. - } - - virtual void dragMoveEvent(QDragMoveEvent* event); - virtual void dropEvent(QDropEvent* event); - virtual void contextMenuEvent(QContextMenuEvent* event); - - virtual void commitData(QWidget* editor); - -private: - void onEjectButtonClicked(PlacesModelItem* item); - void activateRow(int type, const QModelIndex& index); - void showAll(bool show); - -private: - std::shared_ptr model_; - Fm::FilePath currentPath_; - - static std::shared_ptr proxyModel_; // used to hide items in all views -}; - -} - -#endif // FM_PLACESVIEW_H diff --git a/src/proxyfoldermodel.cpp b/src/proxyfoldermodel.cpp deleted file mode 100644 index f66b3a8..0000000 --- a/src/proxyfoldermodel.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 "proxyfoldermodel.h" -#include "foldermodel.h" -#include - -namespace Fm { - -ProxyFolderModel::ProxyFolderModel(QObject* parent): - QSortFilterProxyModel(parent), - showHidden_(false), - backupAsHidden_(true), - folderFirst_(true), - showThumbnails_(false), - thumbnailSize_(0) { - - setDynamicSortFilter(true); - setSortCaseSensitivity(Qt::CaseInsensitive); - - collator_.setNumericMode(true); -} - -ProxyFolderModel::~ProxyFolderModel() { - if(showThumbnails_ && thumbnailSize_ != 0) { - FolderModel* srcModel = static_cast(sourceModel()); - // tell the source model that we don't need the thumnails anymore - if(srcModel) { - srcModel->releaseThumbnails(thumbnailSize_); - disconnect(srcModel, SIGNAL(thumbnailLoaded(QModelIndex, int))); - } - } -} - -void ProxyFolderModel::setSourceModel(QAbstractItemModel* model) { - if(model == sourceModel()) // avoid setting the same model twice - return; - FolderModel* oldSrcModel = static_cast(sourceModel()); -#if (QT_VERSION == QT_VERSION_CHECK(5,11,0)) - // workaround for Qt-5.11 bug https://bugreports.qt.io/browse/QTBUG-68581 - if(oldSrcModel) { - disconnect(oldSrcModel, SIGNAL(destroyed()), this, SLOT(_q_sourceModelDestroyed())); - } -#endif - if(model) { - // we only support Fm::FolderModel - Q_ASSERT(model->inherits("Fm::FolderModel")); - - if(showThumbnails_ && thumbnailSize_ != 0) { // if we're showing thumbnails - if(oldSrcModel) { // we need to release cached thumbnails for the old source model - oldSrcModel->releaseThumbnails(thumbnailSize_); - disconnect(oldSrcModel, SIGNAL(thumbnailLoaded(QModelIndex, int))); - } - FolderModel* newSrcModel = static_cast(model); - if(newSrcModel) { // tell the new source model that we want thumbnails of this size - newSrcModel->cacheThumbnails(thumbnailSize_); - connect(newSrcModel, &FolderModel::thumbnailLoaded, this, &ProxyFolderModel::onThumbnailLoaded); - } - } - } - QSortFilterProxyModel::setSourceModel(model); -} - -void ProxyFolderModel::sort(int column, Qt::SortOrder order) { - int oldColumn = sortColumn(); - Qt::SortOrder oldOrder = sortOrder(); - QSortFilterProxyModel::sort(column, order); - if(column != oldColumn || order != oldOrder) { - Q_EMIT sortFilterChanged(); - } -} - -void ProxyFolderModel::setShowHidden(bool show) { - if(show != showHidden_) { - showHidden_ = show; - invalidateFilter(); - Q_EMIT sortFilterChanged(); - } -} - -void ProxyFolderModel::setBackupAsHidden(bool backupAsHidden) { - if(backupAsHidden != backupAsHidden_) { - backupAsHidden_ = backupAsHidden; - invalidateFilter(); - Q_EMIT sortFilterChanged(); - } -} - -// need to call invalidateFilter() manually. -void ProxyFolderModel::setFolderFirst(bool folderFirst) { - if(folderFirst != folderFirst_) { - folderFirst_ = folderFirst; - invalidate(); - Q_EMIT sortFilterChanged(); - } -} - -void ProxyFolderModel::setSortCaseSensitivity(Qt::CaseSensitivity cs) { - collator_.setCaseSensitivity(cs); - QSortFilterProxyModel::setSortCaseSensitivity(cs); - invalidate(); - Q_EMIT sortFilterChanged(); -} - -bool ProxyFolderModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const { - if(!showHidden_) { - if(FolderModel* srcModel = static_cast(sourceModel())) { - auto info = srcModel->fileInfoFromIndex(srcModel->index(source_row, 0, source_parent)); - if(info && (info->isHidden() || (backupAsHidden_ && info->isBackup()))) { - return false; - } - } - } - // apply additional filters if there're any - for(ProxyFolderModelFilter* const filter : qAsConst(filters_)) { - if(FolderModel* srcModel = static_cast(sourceModel())){ - auto fileInfo = srcModel->fileInfoFromIndex(srcModel->index(source_row, 0, source_parent)); - if(!filter->filterAcceptsRow(this, fileInfo)) { - return false; - } - } - } - return true; -} - -bool ProxyFolderModel::lessThan(const QModelIndex& left, const QModelIndex& right) const { - FolderModel* srcModel = static_cast(sourceModel()); - // left and right are indexes of source model, not the proxy model. - if(srcModel) { - auto leftInfo = srcModel->fileInfoFromIndex(left); - auto rightInfo = srcModel->fileInfoFromIndex(right); - - if(folderFirst_) { - bool leftIsFolder = leftInfo->isDir(); - bool rightIsFolder = rightInfo->isDir(); - if(leftIsFolder != rightIsFolder) { - return sortOrder() == Qt::AscendingOrder ? leftIsFolder : rightIsFolder; - } - } - - int comp; - switch(sortColumn()) { - case FolderModel::ColumnFileMTime: - comp = leftInfo->mtime() - rightInfo->mtime(); - break; - case FolderModel::ColumnFileSize: - comp = leftInfo->size() - rightInfo->size(); - break; - default: { - QString leftText = left.data(Qt::DisplayRole).toString(); - QString rightText = right.data(Qt::DisplayRole).toString(); - comp = collator_.compare(leftText, rightText); - break; - } - } - // always sort files by their display names when they have the same property - if(comp == 0) { - return collator_.compare(leftInfo->displayName(), rightInfo->displayName()) < 0; - } - return comp < 0; - } - return QSortFilterProxyModel::lessThan(left, right); -} - -std::shared_ptr ProxyFolderModel::fileInfoFromIndex(const QModelIndex& index) const { - if(index.isValid()) { - FolderModel* srcModel = static_cast(sourceModel()); - if(srcModel) { - QModelIndex srcIndex = mapToSource(index); - return srcModel->fileInfoFromIndex(srcIndex); - } - } - return nullptr; -} - -QModelIndex ProxyFolderModel::indexFromPath(const FilePath &path) const { - QModelIndex ret; - int n_rows = rowCount(); - for(int row = 0; row < n_rows; ++row) { - auto idx = index(row, FolderModel::ColumnFileName, QModelIndex()); - auto fi = fileInfoFromIndex(idx); - if(fi && fi->path() == path) { // found the item - ret = idx; - break; - } - } - return ret; -} - -std::shared_ptr ProxyFolderModel::fileInfoFromPath(const FilePath &path) const { - return fileInfoFromIndex(indexFromPath(path)); -} - -void ProxyFolderModel::setCutFiles(const QItemSelection& selection) { - FolderModel* srcModel = static_cast(sourceModel()); - if(srcModel) { - srcModel->setCutFiles(mapSelectionToSource(selection)); - } -} - -void ProxyFolderModel::setShowThumbnails(bool show) { - if(show != showThumbnails_) { - showThumbnails_ = show; - FolderModel* srcModel = static_cast(sourceModel()); - if(srcModel && thumbnailSize_ != 0) { - if(show) { - // ask for cache of thumbnails of the new size in source model - srcModel->cacheThumbnails(thumbnailSize_); - // connect to the srcModel so we can be notified when a thumbnail is loaded. - connect(srcModel, &FolderModel::thumbnailLoaded, this, &ProxyFolderModel::onThumbnailLoaded); - } - else { // turn off thumbnails - // free cached old thumbnails in souce model - srcModel->releaseThumbnails(thumbnailSize_); - disconnect(srcModel, SIGNAL(thumbnailLoaded(QModelIndex, int))); - } - // reload all items, FIXME: can we only update items previously having thumbnails - Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0)); - } - } -} - -void ProxyFolderModel::setThumbnailSize(int size) { - if(size != thumbnailSize_) { - FolderModel* srcModel = static_cast(sourceModel()); - if(showThumbnails_ && srcModel) { - // free cached thumbnails of the old size - if(thumbnailSize_ != 0) { - srcModel->releaseThumbnails(thumbnailSize_); - } - else { - // if the old thumbnail size is 0, we did not turn on thumbnail initially - connect(srcModel, &FolderModel::thumbnailLoaded, this, &ProxyFolderModel::onThumbnailLoaded); - } - // ask for cache of thumbnails of the new size in source model - srcModel->cacheThumbnails(size); - // reload all items, FIXME: can we only update items previously having thumbnails - Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0)); - } - - thumbnailSize_ = size; - } -} - -QVariant ProxyFolderModel::data(const QModelIndex& index, int role) const { - if(index.column() == 0) { // only show the decoration role for the first column - if(role == Qt::DecorationRole && showThumbnails_ && thumbnailSize_) { - // we need to show thumbnails instead of icons - FolderModel* srcModel = static_cast(sourceModel()); - QModelIndex srcIndex = mapToSource(index); - QImage image = srcModel->thumbnailFromIndex(srcIndex, thumbnailSize_); - if(!image.isNull()) { // if we got a thumbnail of the desired size, use it - return QVariant(image); - } - } - } - // fallback to icons if thumbnails are not available - return QSortFilterProxyModel::data(index, role); -} - -void ProxyFolderModel::onThumbnailLoaded(const QModelIndex& srcIndex, int size) { - // FolderModel* srcModel = static_cast(sourceModel()); - // FolderModelItem* item = srcModel->itemFromIndex(srcIndex); - // qDebug("ProxyFolderModel::onThumbnailLoaded: %d, %s", size, item->displayName.toUtf8().data()); - - if(size == thumbnailSize_ // if a thumbnail of the size we want is loaded - && srcIndex.model() == sourceModel()) { // check if the sourse model contains the index item - QModelIndex index = mapFromSource(srcIndex); - Q_EMIT dataChanged(index, index); - } -} - -void ProxyFolderModel::addFilter(ProxyFolderModelFilter* filter) { - filters_.append(filter); - invalidateFilter(); - Q_EMIT sortFilterChanged(); -} - -void ProxyFolderModel::removeFilter(ProxyFolderModelFilter* filter) { - filters_.removeOne(filter); - invalidateFilter(); - Q_EMIT sortFilterChanged(); -} - -void ProxyFolderModel::updateFilters() { - invalidate(); - Q_EMIT sortFilterChanged(); -} - -#if 0 -void ProxyFolderModel::reloadAllThumbnails() { - // reload all thumbnails and update UI - FolderModel* srcModel = static_cast(sourceModel()); - if(srcModel) { - int rows = rowCount(); - for(int row = 0; row < rows; ++row) { - QModelIndex index = this->index(row, 0); - QModelIndex srcIndex = mapToSource(index); - QImage image = srcModel->thumbnailFromIndex(srcIndex, size); - // tell the world that the item is changed to trigger a UI update - if(!image.isNull()) { - Q_EMIT dataChanged(index, index); - } - } - } -} -#endif - - -} // namespace Fm diff --git a/src/proxyfoldermodel.h b/src/proxyfoldermodel.h deleted file mode 100644 index 8949f5f..0000000 --- a/src/proxyfoldermodel.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_PROXYFOLDERMODEL_H -#define FM_PROXYFOLDERMODEL_H - -#include "libfmqtglobals.h" -#include -#include -#include -#include - -#include "core/fileinfo.h" - -namespace Fm { - -// a proxy model used to sort and filter FolderModel - -class FolderModelItem; -class ProxyFolderModel; - -class LIBFM_QT_API ProxyFolderModelFilter { -public: - virtual bool filterAcceptsRow(const ProxyFolderModel* model, const std::shared_ptr& info) const = 0; - virtual ~ProxyFolderModelFilter() {} -}; - - -class LIBFM_QT_API ProxyFolderModel : public QSortFilterProxyModel { - Q_OBJECT -public: - explicit ProxyFolderModel(QObject* parent = 0); - virtual ~ProxyFolderModel(); - - // only Fm::FolderModel is allowed for being sourceModel - virtual void setSourceModel(QAbstractItemModel* model); - - void setShowHidden(bool show); - bool showHidden() const { - return showHidden_; - } - - void setBackupAsHidden(bool backupAsHidden); - bool backupAsHidden() const { - return backupAsHidden_; - } - - void setFolderFirst(bool folderFirst); - bool folderFirst() { - return folderFirst_; - } - - void setSortCaseSensitivity(Qt::CaseSensitivity cs); - - void setCutFiles(const QItemSelection& selection); - - bool showThumbnails() { - return showThumbnails_; - } - void setShowThumbnails(bool show); - - int thumbnailSize() { - return thumbnailSize_; - } - void setThumbnailSize(int size); - - std::shared_ptr fileInfoFromIndex(const QModelIndex& index) const; - - std::shared_ptr fileInfoFromPath(const FilePath& path) const; - - QModelIndex indexFromPath(const FilePath& path) const; - - virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); - virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; - - void addFilter(ProxyFolderModelFilter* filter); - void removeFilter(ProxyFolderModelFilter* filter); - void updateFilters(); - -Q_SIGNALS: - void sortFilterChanged(); - -protected Q_SLOTS: - void onThumbnailLoaded(const QModelIndex& srcIndex, int size); - -protected: - bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const; - bool lessThan(const QModelIndex& left, const QModelIndex& right) const; - // void reloadAllThumbnails(); - -private: - QCollator collator_; - bool showHidden_; - bool backupAsHidden_; - bool folderFirst_; - bool showThumbnails_; - int thumbnailSize_; - QList filters_; -}; - -} - -#endif // FM_PROXYFOLDERMODEL_H diff --git a/src/rename-dialog.ui b/src/rename-dialog.ui deleted file mode 100644 index 2b0c123..0000000 --- a/src/rename-dialog.ui +++ /dev/null @@ -1,204 +0,0 @@ - - - RenameDialog - - - - 0 - 0 - 398 - 220 - - - - Confirm to replace files - - - false - - - - 6 - - - 10 - - - - - - 0 - 0 - - - - <html><head/><body><p><span style=" font-weight:600;">There is already a file with the same name in this location.</span></p><p>Do you want to replace the existing file?</p></body></html> - - - - - - - 12 - - - 6 - - - - - - 0 - 0 - - - - dest - - - - - - - with the following file? - - - - - - - - 0 - 0 - - - - src file info - - - - - - - - 0 - 0 - - - - dest file info - - - - - - - - 0 - 0 - - - - src - - - - - - - - - 12 - - - - - - 0 - 0 - - - - &File name: - - - fileName - - - - - - - - - - - - Apply this option to all existing files - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 0 - 0 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ignore|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - RenameDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - RenameDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/renamedialog.cpp b/src/renamedialog.cpp deleted file mode 100644 index a91ad6d..0000000 --- a/src/renamedialog.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 "renamedialog.h" -#include "ui_rename-dialog.h" -#include -#include -#include - -#include "core/iconinfo.h" -#include "utilities.h" - -namespace Fm { - -RenameDialog::RenameDialog(const FileInfo &src, const FileInfo &dest, QWidget* parent, Qt::WindowFlags f): - QDialog(parent, f), - action_(ActionIgnore), - applyToAll_(false) { - - ui = new Ui::RenameDialog(); - ui->setupUi(this); - - auto path = dest.path(); - auto srcIcon = src.icon(); - auto destIcon = dest.icon(); - - // show info for the source file - QIcon icon = srcIcon->qicon(); - // FIXME: deprecate fm_config - QSize iconSize(fm_config->big_icon_size, fm_config->big_icon_size); - QPixmap pixmap = icon.pixmap(iconSize); - ui->srcIcon->setPixmap(pixmap); - - QString infoStr; - // FIXME: deprecate fm_config - auto disp_size = Fm::formatFileSize(src.size(), fm_config->si_unit); - auto srcMtime = QDateTime::fromMSecsSinceEpoch(src.mtime() * 1000).toString(Qt::SystemLocaleShortDate); - if(!disp_size.isEmpty()) { - infoStr = QString(tr("Type: %1\nSize: %2\nModified: %3")) - .arg(src.description()) - .arg(disp_size) - .arg(srcMtime); - } - else { - infoStr = QString(tr("Type: %1\nModified: %2")) - .arg(src.description()) - .arg(srcMtime); - } - ui->srcInfo->setText(infoStr); - - // show info for the dest file - icon = destIcon->qicon(); - pixmap = icon.pixmap(iconSize); - ui->destIcon->setPixmap(pixmap); - - disp_size = Fm::formatFileSize(dest.size(), fm_config->si_unit); - auto destMtime = QDateTime::fromMSecsSinceEpoch(dest.mtime() * 1000).toString(Qt::SystemLocaleShortDate); - if(!disp_size.isEmpty()) { - infoStr = QString(tr("Type: %1\nSize: %2\nModified: %3")) - .arg(dest.description()) - .arg(disp_size) - .arg(destMtime); - } - else { - infoStr = QString(tr("Type: %1\nModified: %2")) - .arg(dest.description()) - .arg(destMtime); - } - ui->destInfo->setText(infoStr); - - auto basename = path.baseName(); - ui->fileName->setText(QString::fromUtf8(basename.get())); - oldName_ = basename.get(); - connect(ui->fileName, &QLineEdit::textChanged, this, &RenameDialog::onFileNameChanged); - - // add "Rename" button - QAbstractButton* button = ui->buttonBox->button(QDialogButtonBox::Ok); - button->setText(tr("&Overwrite")); - // FIXME: there seems to be no way to place the Rename button next to the overwrite one. - renameButton_ = ui->buttonBox->addButton(tr("&Rename"), QDialogButtonBox::ActionRole); - connect(renameButton_, &QPushButton::clicked, this, &RenameDialog::onRenameClicked); - renameButton_->setEnabled(false); // disabled by default - - button = ui->buttonBox->button(QDialogButtonBox::Ignore); - connect(button, &QPushButton::clicked, this, &RenameDialog::onIgnoreClicked); -} - -RenameDialog::~RenameDialog() { - delete ui; -} - -void RenameDialog::onRenameClicked() { - action_ = ActionRename; - QDialog::done(QDialog::Accepted); -} - -void RenameDialog::onIgnoreClicked() { - action_ = ActionIgnore; -} - -// the overwrite button -void RenameDialog::accept() { - action_ = ActionOverwrite; - applyToAll_ = ui->applyToAll->isChecked(); - QDialog::accept(); -} - -// cancel, or close the dialog -void RenameDialog::reject() { - action_ = ActionCancel; - QDialog::reject(); -} - -void RenameDialog::onFileNameChanged(QString newName) { - newName_ = newName; - // FIXME: check if the name already exists in the current dir - bool hasNewName = (newName_ != oldName_); - renameButton_->setEnabled(hasNewName); - renameButton_->setDefault(hasNewName); - - // change default button to rename rather than overwrire - // if the user typed a new filename - QPushButton* overwriteButton = static_cast(ui->buttonBox->button(QDialogButtonBox::Ok)); - overwriteButton->setEnabled(!hasNewName); - overwriteButton->setDefault(!hasNewName); -} - - -} // namespace Fm diff --git a/src/renamedialog.h b/src/renamedialog.h deleted file mode 100644 index b893177..0000000 --- a/src/renamedialog.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_RENAMEDIALOG_H -#define FM_RENAMEDIALOG_H - -#include "libfmqtglobals.h" -#include -#include - -#include "core/fileinfo.h" - -namespace Ui { -class RenameDialog; -} - -class QPushButton; - -namespace Fm { - -class LIBFM_QT_API RenameDialog : public QDialog { - Q_OBJECT - -public: - enum Action { - ActionCancel, - ActionRename, - ActionOverwrite, - ActionIgnore - }; - -public: - explicit RenameDialog(const FileInfo &src, const FileInfo &dest, QWidget* parent = 0, Qt::WindowFlags f = 0); - virtual ~RenameDialog(); - - Action action() { - return action_; - } - - bool applyToAll() { - return applyToAll_; - } - - QString newName() { - return newName_; - } - -protected Q_SLOTS: - void onRenameClicked(); - void onIgnoreClicked(); - void onFileNameChanged(QString newName); - -protected: - void accept(); - void reject(); - -private: - Ui::RenameDialog* ui; - QPushButton* renameButton_; - Action action_; - bool applyToAll_; - QString oldName_; - QString newName_; -}; - -} - -#endif // FM_RENAMEDIALOG_H diff --git a/src/sidepane.cpp b/src/sidepane.cpp deleted file mode 100644 index 60fccb1..0000000 --- a/src/sidepane.cpp +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 "sidepane.h" -#include -#include -#include -#include "placesview.h" -#include "dirtreeview.h" -#include "dirtreemodel.h" -#include "filemenu.h" - -namespace Fm { - -SidePane::SidePane(QWidget* parent): - QWidget(parent), - view_(nullptr), - combo_(nullptr), - iconSize_(24, 24), - mode_(ModeNone), - showHidden_(false) { - - verticalLayout = new QVBoxLayout(this); - verticalLayout->setContentsMargins(0, 0, 0, 0); - - combo_ = new QComboBox(this); - combo_->setFrame(false); - combo_->addItem(tr("Places")); - combo_->addItem(tr("Directory Tree")); - connect(combo_, static_cast(&QComboBox::currentIndexChanged), this, &SidePane::onComboCurrentIndexChanged); - verticalLayout->addWidget(combo_); -} - -SidePane::~SidePane() { - // qDebug("delete SidePane"); -} - -void SidePane::onComboCurrentIndexChanged(int current) { - if(current != mode_) { - setMode(Mode(current)); - } -} - -void SidePane::setIconSize(QSize size) { - iconSize_ = size; - switch(mode_) { - case ModePlaces: - static_cast(view_)->setIconSize(size); - /* Falls through. */ - case ModeDirTree: - static_cast(view_)->setIconSize(size); - break; - default: - ; - } -} - -void SidePane::setCurrentPath(Fm::FilePath path) { - Q_ASSERT(path); - currentPath_ = std::move(path); - switch(mode_) { - case ModePlaces: - static_cast(view_)->setCurrentPath(currentPath_); - break; - case ModeDirTree: - static_cast(view_)->setCurrentPath(currentPath_); - break; - default: - ; - } -} - -SidePane::Mode SidePane::modeByName(const char* str) { - if(str == nullptr) { - return ModeNone; - } - if(strcmp(str, "places") == 0) { - return ModePlaces; - } - if(strcmp(str, "dirtree") == 0) { - return ModeDirTree; - } - return ModeNone; -} - -const char* SidePane::modeName(SidePane::Mode mode) { - switch(mode) { - case ModePlaces: - return "places"; - case ModeDirTree: - return "dirtree"; - default: - return nullptr; - } -} - -bool SidePane::setHomeDir(const char* /*home_dir*/) { - if(view_ == nullptr) { - return false; - } - // TODO: SidePane::setHomeDir - - switch(mode_) { - case ModePlaces: - // static_cast(view_); - return true; - case ModeDirTree: - // static_cast(view_); - return true; - default: - ; - } - return true; -} - -void SidePane::initDirTree() { - DirTreeModel* model = new DirTreeModel(view_); - model->setShowHidden(showHidden_); - - Fm::FilePathList rootPaths; - rootPaths.emplace_back(Fm::FilePath::homeDir()); - rootPaths.emplace_back(Fm::FilePath::fromLocalPath("/")); - model->addRoots(std::move(rootPaths)); - static_cast(view_)->setModel(model); -} - -void SidePane::setMode(Mode mode) { - if(mode == mode_) { - return; - } - - if(view_) { - delete view_; - view_ = nullptr; - //if(sp->update_popup) - // g_signal_handlers_disconnect_by_func(sp->view, on_item_popup, sp); - } - mode_ = mode; - - combo_->setCurrentIndex(mode); - switch(mode) { - case ModePlaces: { - PlacesView* placesView = new Fm::PlacesView(this); - view_ = placesView; - placesView->restoreHiddenItems(restorableHiddenPlaces_); - placesView->setIconSize(iconSize_); - placesView->setCurrentPath(currentPath_); - connect(placesView, &PlacesView::chdirRequested, this, &SidePane::chdirRequested); - connect(placesView, &PlacesView::hiddenItemSet, this, &SidePane::hiddenPlaceSet); - break; - } - case ModeDirTree: { - DirTreeView* dirTreeView = new Fm::DirTreeView(this); - view_ = dirTreeView; - initDirTree(); - dirTreeView->setIconSize(iconSize_); - dirTreeView->setCurrentPath(currentPath_); - connect(dirTreeView, &DirTreeView::chdirRequested, this, &SidePane::chdirRequested); - connect(dirTreeView, &DirTreeView::openFolderInNewWindowRequested, - this, &SidePane::openFolderInNewWindowRequested); - connect(dirTreeView, &DirTreeView::openFolderInNewTabRequested, - this, &SidePane::openFolderInNewTabRequested); - connect(dirTreeView, &DirTreeView::openFolderInTerminalRequested, - this, &SidePane::openFolderInTerminalRequested); - connect(dirTreeView, &DirTreeView::createNewFolderRequested, - this, &SidePane::createNewFolderRequested); - connect(dirTreeView, &DirTreeView::prepareFileMenu, - this, &SidePane::prepareFileMenu); - break; - } - default: - ; - } - if(view_) { - // if(sp->update_popup) - // g_signal_connect(sp->view, "item-popup", G_CALLBACK(on_item_popup), sp); - verticalLayout->addWidget(view_); - } - Q_EMIT modeChanged(mode); -} - -void SidePane::setShowHidden(bool show_hidden) { - if(view_ == nullptr || show_hidden == showHidden_) { - return; - } - showHidden_ = show_hidden; - if(mode_ == ModeDirTree) { - DirTreeView* dirTreeView = static_cast(view_); - DirTreeModel* model = static_cast(dirTreeView->model()); - if(model) { - model->setShowHidden(showHidden_); - } - } -} - -void SidePane::restoreHiddenPlaces(const QSet& items) { - if(mode_ == ModePlaces) { - static_cast(view_)->restoreHiddenItems(items); - } - else { - restorableHiddenPlaces_.unite(items); - } -} - -} // namespace Fm diff --git a/src/sidepane.h b/src/sidepane.h deleted file mode 100644 index c2b5201..0000000 --- a/src/sidepane.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_SIDEPANE_H -#define FM_SIDEPANE_H - -#include "libfmqtglobals.h" -#include -#include - -#include "core/filepath.h" - -class QComboBox; -class QVBoxLayout; -class QWidget; - -namespace Fm { - -class FileMenu; - -class LIBFM_QT_API SidePane : public QWidget { - Q_OBJECT - -public: - enum Mode { - ModeNone = -1, - ModePlaces = 0, - ModeDirTree, - NumModes - }; - -public: - explicit SidePane(QWidget* parent = 0); - virtual ~SidePane(); - - QSize iconSize() const { - return iconSize_; - } - - void setIconSize(QSize size); - - const Fm::FilePath& currentPath() const { - return currentPath_; - } - - void setCurrentPath(Fm::FilePath path); - - void setMode(Mode mode); - - Mode mode() const { - return mode_; - } - - QWidget* view() const { - return view_; - } - - static const char* modeName(Mode mode); - - static Mode modeByName(const char* str); - -#if 0 // FIXME: are these APIs from libfm-qt needed? - int modeCount(void) { - return NumModes; - } - - QString modeLabel(Mode mode); - - QString modeTooltip(Mode mode); -#endif - - void setShowHidden(bool show_hidden); - - bool showHidden() const { - return showHidden_; - } - - bool setHomeDir(const char* home_dir); - - void chdir(Fm::FilePath path) { - setCurrentPath(std::move(path)); - } - - void restoreHiddenPlaces(const QSet& items); - -Q_SIGNALS: - void chdirRequested(int type, const Fm::FilePath& path); - void openFolderInNewWindowRequested(const Fm::FilePath& path); - void openFolderInNewTabRequested(const Fm::FilePath& path); - void openFolderInTerminalRequested(const Fm::FilePath& path); - void createNewFolderRequested(const Fm::FilePath& path); - void modeChanged(Fm::SidePane::Mode mode); - - void prepareFileMenu(Fm::FileMenu* menu); // emit before showing a Fm::FileMenu - - void hiddenPlaceSet(const QString& str, bool hide); - -protected Q_SLOTS: - void onComboCurrentIndexChanged(int current); - -private: - void initDirTree(); - -private: - Fm::FilePath currentPath_; - QWidget* view_; - QComboBox* combo_; - QVBoxLayout* verticalLayout; - QSize iconSize_; - Mode mode_; - bool showHidden_; - QSet restorableHiddenPlaces_; -}; - -} - -#endif // FM_SIDEPANE_H diff --git a/src/tests/test-filedialog.cpp b/src/tests/test-filedialog.cpp deleted file mode 100644 index ccd1809..0000000 --- a/src/tests/test-filedialog.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include -#include -#include "../core/folder.h" -#include "../foldermodel.h" -#include "../folderview.h" -#include "../cachedfoldermodel.h" -#include "../proxyfoldermodel.h" -#include "../pathedit.h" -#include "../filedialog.h" -#include "libfmqt.h" - - -int main(int argc, char** argv) { - QApplication app(argc, argv); - - Fm::LibFmQt contex; - - /* - QFileDialog dlg0; - dlg0.setFileMode(QFileDialog::ExistingFiles); - - dlg0.setNameFilters(QStringList() << "Txt (*.txt)"); - QObject::connect(&dlg0, &QFileDialog::currentChanged, [](const QString& path) { - qDebug() << "currentChanged:" << path; - }); - QObject::connect(&dlg0, &QFileDialog::fileSelected, [](const QString& path) { - qDebug() << "fileSelected:" << path; - }); - QObject::connect(&dlg0, &QFileDialog::filesSelected, [](const QStringList& paths) { - qDebug() << "filesSelected:" << paths; - }); - - dlg0.exec(); - */ - - Fm::FileDialog dlg; - // dlg.setFileMode(QFileDialog::ExistingFile); - dlg.setFileMode(QFileDialog::ExistingFiles); - // dlg.setFileMode(QFileDialog::Directory); - dlg.setNameFilters(QStringList() << "All (*)" << "Text (*.txt)" << "Images (*.gif *.jpeg *.jpg)"); - - dlg.exec(); - qDebug() << "selected files:" << dlg.selectedFiles(); - - return 0; -} diff --git a/src/tests/test-folder.cpp b/src/tests/test-folder.cpp deleted file mode 100644 index 45af775..0000000 --- a/src/tests/test-folder.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include -#include "../core/folder.h" - -int main(int argc, char** argv) { - QApplication app(argc, argv); - - auto home = Fm::FilePath::homeDir(); - auto folder = Fm::Folder::fromPath(home); - - QObject::connect(folder.get(), &Fm::Folder::startLoading, [=]() { - qDebug("start loading"); - }); - QObject::connect(folder.get(), &Fm::Folder::finishLoading, [=]() { - qDebug("finish loading"); - }); - - QObject::connect(folder.get(), &Fm::Folder::filesAdded, [=](Fm::FileInfoList& files) { - qDebug("files added"); - for(auto& item: files) { - qDebug() << item->displayName(); - } - }); - QObject::connect(folder.get(), &Fm::Folder::filesRemoved, [=](Fm::FileInfoList& files) { - qDebug("files removed"); - for(auto& item: files) { - qDebug() << item->displayName(); - } - }); - QObject::connect(folder.get(), &Fm::Folder::filesChanged, [=](std::vector& file_pairs) { - qDebug("files changed"); - for(auto& pair: file_pairs) { - auto& item = pair.second; - qDebug() << item->displayName(); - } - }); - - for(auto& item: folder->files()) { - qDebug() << item->displayName(); - } - qDebug() << "here"; - - return app.exec(); -} diff --git a/src/tests/test-folderview.cpp b/src/tests/test-folderview.cpp deleted file mode 100644 index 427f1e5..0000000 --- a/src/tests/test-folderview.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include -#include -#include "../core/folder.h" -#include "../foldermodel.h" -#include "../folderview.h" -#include "../cachedfoldermodel.h" -#include "../proxyfoldermodel.h" -#include "../pathedit.h" -#include "libfmqt.h" - -int main(int argc, char** argv) { - QApplication app(argc, argv); - - Fm::LibFmQt contex; - QMainWindow win; - - Fm::FolderView folder_view; - win.setCentralWidget(&folder_view); - - auto home = Fm::FilePath::homeDir(); - Fm::CachedFolderModel* model = Fm::CachedFolderModel::modelFromPath(home); - auto proxy_model = new Fm::ProxyFolderModel(); - proxy_model->sort(Fm::FolderModel::ColumnFileName, Qt::AscendingOrder); - proxy_model->setSourceModel(model); - - proxy_model->setThumbnailSize(64); - proxy_model->setShowThumbnails(true); - - folder_view.setModel(proxy_model); - - QToolBar toolbar; - win.addToolBar(Qt::TopToolBarArea, &toolbar); - Fm::PathEdit edit; - edit.setText(home.toString().get()); - toolbar.addWidget(&edit); - auto action = new QAction("Go", nullptr); - toolbar.addAction(action); - QObject::connect(action, &QAction::triggered, [&]() { - auto path = Fm::FilePath::fromPathStr(edit.text().toLocal8Bit().constData()); - auto new_model = Fm::CachedFolderModel::modelFromPath(path); - proxy_model->setSourceModel(new_model); - }); - - win.show(); - return app.exec(); -} diff --git a/src/tests/test-placesview.cpp b/src/tests/test-placesview.cpp deleted file mode 100644 index ddfe213..0000000 --- a/src/tests/test-placesview.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include -#include -#include -#include -#include -#include "../placesview.h" -#include "libfmqt.h" - -int main(int argc, char** argv) { - QApplication app(argc, argv); - - Fm::LibFmQt contex; - QMainWindow win; - - Fm::PlacesView view; - win.setCentralWidget(&view); - - win.show(); - return app.exec(); -} diff --git a/src/tests/test-volumemanager.cpp b/src/tests/test-volumemanager.cpp deleted file mode 100644 index 7b6438a..0000000 --- a/src/tests/test-volumemanager.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include -#include "../core/volumemanager.h" - -int main(int argc, char** argv) { - QApplication app(argc, argv); - - auto vm = Fm::VolumeManager::globalInstance(); - - QObject::connect(vm.get(), &Fm::VolumeManager::volumeAdded, [=](const Fm::Volume& vol) { - qDebug() << "volume added:" << vol.name().get(); - }); - QObject::connect(vm.get(), &Fm::VolumeManager::volumeRemoved, [=](const Fm::Volume& vol) { - qDebug() << "volume removed:" << vol.name().get(); - }); - - for(auto& item: vm->volumes()) { - auto name = item.name(); - qDebug() << "list volume:" << name.get(); - } - - return app.exec(); -} diff --git a/src/utilities.cpp b/src/utilities.cpp deleted file mode 100644 index 8820587..0000000 --- a/src/utilities.cpp +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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 "utilities.h" -#include "utilities_p.h" -#include -#include -#include -#include -#include -#include -#include -#include "fileoperation.h" -#include - -#include -#include -#include -#include - -namespace Fm { - -Fm::FilePathList pathListFromUriList(const char* uriList) { - Fm::FilePathList pathList; - char** uris = g_strsplit_set(uriList, "\r\n", -1); - for(char** uri = uris; *uri; ++uri) { - if(**uri != '\0') { - pathList.push_back(Fm::FilePath::fromUri(*uri)); - } - } - g_strfreev(uris); - return pathList; -} - -QByteArray pathListToUriList(const Fm::FilePathList& paths) { - QByteArray uriList; - for(auto& path: paths) { - uriList += path.uri().get(); - uriList += "\r\n"; - } - return uriList; -} - -Fm::FilePathList pathListFromQUrls(QList urls) { - Fm::FilePathList pathList; - for(auto it = urls.cbegin(); it != urls.cend(); ++it) { - auto path = Fm::FilePath::fromUri(it->toString().toUtf8().constData()); - pathList.push_back(std::move(path)); - } - return pathList; -} - -std::pair parseClipboardData(const QMimeData& data) { - bool isCut = false; - Fm::FilePathList paths; - - if(data.hasFormat("x-special/gnome-copied-files")) { - // Gnome, LXDE, and XFCE - QByteArray gnomeData = data.data("x-special/gnome-copied-files"); - char* pdata = gnomeData.data(); - char* eol = strchr(pdata, '\n'); - - if(eol) { - *eol = '\0'; - isCut = (strcmp(pdata, "cut") == 0 ? true : false); - paths = pathListFromUriList(eol + 1); - } - } - - if(paths.empty() && data.hasUrls()) { - // The KDE way - paths = Fm::pathListFromQUrls(data.urls()); - QByteArray cut = data.data(QStringLiteral("application/x-kde-cutselection")); - if(!cut.isEmpty() && QChar::fromLatin1(cut.at(0)) == QLatin1Char('1')) { - isCut = true; - } - } - - return std::make_pair(paths, isCut); -} - -void pasteFilesFromClipboard(const Fm::FilePath& destPath, QWidget* parent) { - QClipboard* clipboard = QApplication::clipboard(); - const QMimeData* data = clipboard->mimeData(); - Fm::FilePathList paths; - bool isCut = false; - - std::tie(paths, isCut) = parseClipboardData(*data); - - if(!paths.empty()) { - if(isCut) { - FileOperation::moveFiles(paths, destPath, parent); - clipboard->clear(QClipboard::Clipboard); - } - else { - FileOperation::copyFiles(paths, destPath, parent); - } - } -} - -void copyFilesToClipboard(const Fm::FilePathList& files) { - QClipboard* clipboard = QApplication::clipboard(); - QMimeData* data = new QMimeData(); - QByteArray ba; - auto urilist = pathListToUriList(files); - - // Add current pid to trace cut/copy operations to current app - data->setData(QStringLiteral("text/x-libfmqt-pid"), ba.setNum(QCoreApplication::applicationPid())); - // Gnome, LXDE, and XFCE - // Note: the standard text/urilist format uses CRLF for line breaks, but gnome format uses LF only - data->setData("x-special/gnome-copied-files", QByteArray("copy\n") + urilist.replace("\r\n", "\n")); - // The KDE way - data->setData("text/uri-list", urilist); - // data->setData(QStringLiteral("application/x-kde-cutselection"), QByteArrayLiteral("0")); - clipboard->setMimeData(data); -} - -void cutFilesToClipboard(const Fm::FilePathList& files) { - QClipboard* clipboard = QApplication::clipboard(); - QMimeData* data = new QMimeData(); - QByteArray ba; - auto urilist = pathListToUriList(files); - - // Add current pid to trace cut/copy operations to current app - data->setData(QStringLiteral("text/x-libfmqt-pid"), ba.setNum(QCoreApplication::applicationPid())); - // Gnome, LXDE, and XFCE - // Note: the standard text/urilist format uses CRLF for line breaks, but gnome format uses LF only - data->setData("x-special/gnome-copied-files", QByteArray("cut\n") + urilist.replace("\r\n", "\n")); - // The KDE way - data->setData("text/uri-list", urilist); - data->setData(QStringLiteral("application/x-kde-cutselection"), QByteArrayLiteral("1")); - clipboard->setMimeData(data); -} - -bool isCurrentPidClipboardData(const QMimeData& data) { - QByteArray clip_pid = data.data(QStringLiteral("text/x-libfmqt-pid")); - QByteArray curr_pid; - curr_pid.setNum(QCoreApplication::applicationPid()); - - return !clip_pid.isEmpty() && clip_pid == curr_pid; -} - -bool changeFileName(const Fm::FilePath& filePath, const QString& newName, QWidget* parent, bool showMessage) { - auto dest = filePath.parent().child(newName.toLocal8Bit().constData()); - Fm::GErrorPtr err; - if(!g_file_move(filePath.gfile().get(), dest.gfile().get(), - GFileCopyFlags(G_FILE_COPY_ALL_METADATA | - G_FILE_COPY_NO_FALLBACK_FOR_MOVE | - G_FILE_COPY_NOFOLLOW_SYMLINKS), - nullptr, /* make this cancellable later. */ - nullptr, nullptr, &err)) { - if (showMessage){ - QMessageBox::critical(parent, QObject::tr("Error"), err.message()); - } - return false; - } - return true; -} - -bool renameFile(std::shared_ptr file, QWidget* parent) { - FilenameDialog dlg(parent); - dlg.setWindowTitle(QObject::tr("Rename File")); - dlg.setLabelText(QObject::tr("Please enter a new name:")); - // FIXME: what's the best way to handle non-UTF8 filename encoding here? - auto old_name = QString::fromStdString(file->name()); - dlg.setTextValue(old_name); - - if(file->isDir()) { // select filename extension for directories - dlg.setSelectExtension(true); - } - - if(dlg.exec() != QDialog::Accepted) { - return false; // stop multiple renaming - } - - QString new_name = dlg.textValue(); - if(new_name == old_name) { - return true; // let multiple renaming continue - } - changeFileName(file->path(), new_name, parent); - return true; -} - -// templateFile is a file path used as a template of the new file. -void createFileOrFolder(CreateFileType type, FilePath parentDir, const TemplateItem* templ, QWidget* parent) { - QString defaultNewName; - QString prompt; - QString dialogTitle = type == CreateNewFolder ? QObject::tr("Create Folder") - : QObject::tr("Create File"); - - switch(type) { - case CreateNewTextFile: - prompt = QObject::tr("Please enter a new file name:"); - defaultNewName = QObject::tr("New text file"); - break; - - case CreateNewFolder: - prompt = QObject::tr("Please enter a new folder name:"); - defaultNewName = QObject::tr("New folder"); - break; - - case CreateWithTemplate: { - auto mime = templ->mimeType(); - prompt = QObject::tr("Enter a name for the new %1:").arg(mime->desc()); - defaultNewName = QString::fromStdString(templ->name()); - } - break; - } - -_retry: - // ask the user to input a file name - bool ok; - QString new_name = QInputDialog::getText(parent, dialogTitle, - prompt, - QLineEdit::Normal, - defaultNewName, - &ok); - - if(!ok) { - return; - } - - auto dest = parentDir.child(new_name.toLocal8Bit().data()); - Fm::GErrorPtr err; - switch(type) { - case CreateNewTextFile: { - Fm::GFileOutputStreamPtr f{g_file_create(dest.gfile().get(), G_FILE_CREATE_NONE, nullptr, &err), false}; - if(f) { - g_output_stream_close(G_OUTPUT_STREAM(f.get()), nullptr, nullptr); - } - break; - } - case CreateNewFolder: - g_file_make_directory(dest.gfile().get(), nullptr, &err); - break; - case CreateWithTemplate: - // copy the template file to its destination - FileOperation::copyFile(templ->filePath(), dest, parent); - break; - } - if(err) { - if(err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_EXISTS) { - err.reset(); - goto _retry; - } - - QMessageBox::critical(parent, QObject::tr("Error"), err.message()); - } -} - -uid_t uidFromName(QString name) { - uid_t ret; - if(name.isEmpty()) { - return INVALID_UID; - } - if(name.at(0).digitValue() != -1) { - ret = uid_t(name.toUInt()); - } - else { - struct passwd* pw = getpwnam(name.toLatin1()); - // FIXME: use getpwnam_r instead later to make it reentrant - ret = pw ? pw->pw_uid : INVALID_UID; - } - - return ret; -} - -QString uidToName(uid_t uid) { - QString ret; - struct passwd* pw = getpwuid(uid); - - if(pw) { - ret = pw->pw_name; - } - else { - ret = QString::number(uid); - } - - return ret; -} - -gid_t gidFromName(QString name) { - gid_t ret; - if(name.isEmpty()) { - return INVALID_GID; - } - if(name.at(0).digitValue() != -1) { - ret = gid_t(name.toUInt()); - } - else { - // FIXME: use getgrnam_r instead later to make it reentrant - struct group* grp = getgrnam(name.toLatin1()); - ret = grp ? grp->gr_gid : INVALID_GID; - } - - return ret; -} - -QString gidToName(gid_t gid) { - QString ret; - struct group* grp = getgrgid(gid); - - if(grp) { - ret = grp->gr_name; - } - else { - ret = QString::number(gid); - } - - return ret; -} - -int execModelessDialog(QDialog* dlg) { - // FIXME: this does much less than QDialog::exec(). Will this work flawlessly? - QEventLoop loop; - QObject::connect(dlg, &QDialog::finished, &loop, &QEventLoop::quit); - // DialogExec does not seem to be documented in the Qt API doc? - // However, in the source code of QDialog::exec(), it's used so let's use it too. - dlg->show(); - (void)loop.exec(QEventLoop::DialogExec); - return dlg->result(); -} - -// check if GVFS can support this uri scheme (lower case) -// NOTE: this does not work reliably due to some problems in gio/gvfs and causes bug lxqt/lxqt#512 -// https://github.com/lxqt/lxqt/issues/512 -// Use uriExists() whenever possible. -bool isUriSchemeSupported(const char* uriScheme) { - const gchar* const* schemes = g_vfs_get_supported_uri_schemes(g_vfs_get_default()); - if(Q_UNLIKELY(schemes == nullptr)) { - return false; - } - for(const gchar * const* scheme = schemes; *scheme; ++scheme) - if(strcmp(uriScheme, *scheme) == 0) { - return true; - } - return false; -} - -// check if the URI exists. -// NOTE: this is a blocking call possibly involving I/O. -// So it's better to use it in limited cases, like checking trash:// or computer://. -// Avoid calling this on a slow filesystem. -// Checking "network:///" is very slow, for example. -bool uriExists(const char* uri) { - GFile* gf = g_file_new_for_uri(uri); - bool ret = (bool)g_file_query_exists(gf, nullptr); - g_object_unref(gf); - return ret; -} - -QString formatFileSize(uint64_t size, bool useSI) { - Fm::CStrPtr str{g_format_size_full(size, useSI ? G_FORMAT_SIZE_DEFAULT : G_FORMAT_SIZE_IEC_UNITS)}; - return QString(str.get()); -} - -} // namespace Fm diff --git a/src/utilities.h b/src/utilities.h deleted file mode 100644 index bf3c90e..0000000 --- a/src/utilities.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) - * - * 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_UTILITIES_H -#define FM_UTILITIES_H - -#include "libfmqtglobals.h" -#include -#include -#include -#include -#include - -#include -#include - -#include "core/filepath.h" -#include "core/fileinfo.h" -#include "core/templates.h" - -class QDialog; - -namespace Fm { - -LIBFM_QT_API Fm::FilePathList pathListFromUriList(const char* uriList); - -LIBFM_QT_API QByteArray pathListToUriList(const Fm::FilePathList& paths); - -LIBFM_QT_API Fm::FilePathList pathListFromQUrls(QList urls); - -LIBFM_QT_API std::pair parseClipboardData(const QMimeData& data); - -LIBFM_QT_API void pasteFilesFromClipboard(const Fm::FilePath& destPath, QWidget* parent = 0); - -LIBFM_QT_API void copyFilesToClipboard(const Fm::FilePathList& files); - -LIBFM_QT_API void cutFilesToClipboard(const Fm::FilePathList& files); - -LIBFM_QT_API bool isCurrentPidClipboardData(const QMimeData& data); - -LIBFM_QT_API bool changeFileName(const Fm::FilePath& path, const QString& newName, QWidget* parent, bool showMessage = true); - -LIBFM_QT_API bool renameFile(std::shared_ptr file, QWidget* parent = 0); - -enum CreateFileType { - CreateNewFolder, - CreateNewTextFile, - CreateWithTemplate -}; - -LIBFM_QT_API void createFileOrFolder(CreateFileType type, FilePath parentDir, const TemplateItem* templ = nullptr, QWidget* parent = 0); - -constexpr uid_t INVALID_UID = uid_t(-1); - -LIBFM_QT_API uid_t uidFromName(QString name); - -LIBFM_QT_API QString uidToName(uid_t uid); - -constexpr gid_t INVALID_GID = gid_t(-1); - -LIBFM_QT_API gid_t gidFromName(QString name); - -LIBFM_QT_API QString gidToName(gid_t gid); - -LIBFM_QT_API int execModelessDialog(QDialog* dlg); - -// NOTE: this does not work reliably due to some problems in gio/gvfs -// Use uriExists() whenever possible. -LIBFM_QT_API bool isUriSchemeSupported(const char* uriScheme); - -LIBFM_QT_API bool uriExists(const char* uri); - -LIBFM_QT_API QString formatFileSize(std::uint64_t size, bool useSI = false); - -} - -#endif // FM_UTILITIES_H diff --git a/src/utilities_p.h b/src/utilities_p.h deleted file mode 100644 index 7852f99..0000000 --- a/src/utilities_p.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) - * - * 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 __UTILITS_P_H__ -#define __UTILITS_P_H__ - -#include -#include -#include - -namespace Fm { - -// private class used in internal implementation -class FilenameDialog : public QInputDialog { - Q_OBJECT -public: - FilenameDialog(QWidget* parent = 0, Qt::WindowFlags flags = 0): - QInputDialog(parent, flags), - selectExtension_(false) { - } - - virtual void showEvent(QShowEvent * event) { - QWidget::showEvent(event); - if(!selectExtension_) // dot not select filename extension - QTimer::singleShot(0, this, SLOT(initSelection())); - } - - bool selectExtension() const { - return selectExtension_; - } - - void setSelectExtension(bool value) { - selectExtension_ = value; - } - -private Q_SLOTS: - // do not select filename extensions - void initSelection() { - // find the QLineEdit child widget - QLineEdit* lineEdit = findChild(); - if(lineEdit) { - QString filename = lineEdit->text(); - if(!filename.isEmpty()) { - // only select filename part without extension name. - int ext = filename.lastIndexOf('.'); - if(ext != -1) { - // add special cases for tar.gz, tar.bz2, and other tar.* files - if(filename.leftRef(ext).endsWith(".tar")) - ext -= 4; - // FIXME: should we also handle other special cases? - lineEdit->setSelection(0, ext); - } - } - } - } - -private: - bool selectExtension_; -}; - -} // namespace Fm - -#endif diff --git a/src/utils.h b/src/utils.h deleted file mode 100644 index 109146d..0000000 --- a/src/utils.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016 Hong Jen Yee (PCMan) - * - * 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_UTILS_H__ -#define __LIBFM_QT_FM_UTILS_H__ - -#include -#include -#include -#include "libfmqtglobals.h" - - -namespace Fm { - - - -} - -#endif // __LIBFM_QT_FM_UTILS_H__ diff --git a/src/xdndworkaround.cpp b/src/xdndworkaround.cpp deleted file mode 100644 index f0a35d2..0000000 --- a/src/xdndworkaround.cpp +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C) 2016 Hong Jen Yee (PCMan) - * - * 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 -#include "xdndworkaround.h" -#include -#include -#include -#include -#include -#include - -// This part is for Qt >= 5.4 only -#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) -#include -#include -#include - -// these are private Qt headers which are not part of Qt APIs -#include // Too bad that we need to use private headers of Qt :-( - -// For some unknown reasons, the event type constants defined in -// xcb/input.h are different from that in X11/extension/XI2.h -// To be safe, we define it ourselves. -#undef XI_ButtonRelease -#define XI_ButtonRelease 5 - -#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) - -XdndWorkaround::XdndWorkaround() { - if(!QX11Info::isPlatformX11()) { - return; - } - - // we need to filter all X11 events - qApp->installNativeEventFilter(this); - -// This part is for Qt >= 5.4 only -#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) - lastDrag_ = nullptr; - - // initialize xinput2 since newer versions of Qt5 uses it. - static char xi_name[] = "XInputExtension"; - xcb_connection_t* conn = QX11Info::connection(); - xcb_query_extension_cookie_t cookie = xcb_query_extension(conn, strlen(xi_name), xi_name); - xcb_generic_error_t* err = nullptr; - xcb_query_extension_reply_t* reply = xcb_query_extension_reply(conn, cookie, &err); - if(err == nullptr) { - xinput2Enabled_ = true; - xinputOpCode_ = reply->major_opcode; - xinputEventBase_ = reply->first_event; - xinputErrorBase_ = reply->first_error; - // qDebug() << "xinput: " << m_xi2Enabled << m_xiOpCode << m_xiEventBase; - } - else { - xinput2Enabled_ = false; - free(err); - } - free(reply); -#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) -} - -XdndWorkaround::~XdndWorkaround() { - if(!QX11Info::isPlatformX11()) { - return; - } - qApp->removeNativeEventFilter(this); -} - -bool XdndWorkaround::nativeEventFilter(const QByteArray& eventType, void* message, long* /*result*/) { - if(Q_LIKELY(eventType == "xcb_generic_event_t")) { - xcb_generic_event_t* event = static_cast(message); - switch(event->response_type & ~0x80) { - case XCB_CLIENT_MESSAGE: - return clientMessage(reinterpret_cast(event)); - case XCB_SELECTION_NOTIFY: - return selectionNotify(reinterpret_cast(event)); -// This part is for Qt >= 5.4 only -#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) - case XCB_SELECTION_REQUEST: - return selectionRequest(reinterpret_cast(event)); - case XCB_GE_GENERIC: - // newer versions of Qt5 supports xinput2, which sends its mouse events via XGE. - return genericEvent(reinterpret_cast(event)); - case XCB_BUTTON_RELEASE: - // older versions of Qt5 receive mouse events via old XCB events. - buttonRelease(); - break; -#endif // Qt >= 5.4 - default: - break; - } - } - return false; -} - -// static -QByteArray XdndWorkaround::atomName(xcb_atom_t atom) { - QByteArray name; - xcb_connection_t* conn = QX11Info::connection(); - xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(conn, atom); - xcb_get_atom_name_reply_t* reply = xcb_get_atom_name_reply(conn, cookie, nullptr); - int len = xcb_get_atom_name_name_length(reply); - if(len > 0) { - name.append(xcb_get_atom_name_name(reply), len); - } - free(reply); - return name; -} - -// static -xcb_atom_t XdndWorkaround::internAtom(const char* name, int len) { - xcb_atom_t atom = 0; - if(len == -1) { - len = strlen(name); - } - xcb_connection_t* conn = QX11Info::connection(); - xcb_intern_atom_cookie_t cookie = xcb_intern_atom(conn, false, len, name); - xcb_generic_error_t* err = nullptr; - xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(conn, cookie, &err); - if(reply != nullptr) { - atom = reply->atom; - free(reply); - } - if(err != nullptr) { - free(err); - } - return atom; -} - -// static -QByteArray XdndWorkaround::windowProperty(xcb_window_t window, xcb_atom_t propAtom, xcb_atom_t typeAtom, int len) { - QByteArray data; - xcb_connection_t* conn = QX11Info::connection(); - xcb_get_property_cookie_t cookie = xcb_get_property(conn, false, window, propAtom, typeAtom, 0, len); - xcb_generic_error_t* err = nullptr; - xcb_get_property_reply_t* reply = xcb_get_property_reply(conn, cookie, &err); - if(reply != nullptr) { - len = xcb_get_property_value_length(reply); - const char* buf = (const char*)xcb_get_property_value(reply); - data.append(buf, len); - free(reply); - } - if(err != nullptr) { - free(err); - } - return data; -} - -// static -void XdndWorkaround::setWindowProperty(xcb_window_t window, xcb_atom_t propAtom, xcb_atom_t typeAtom, void* data, int len, int format) { - xcb_connection_t* conn = QX11Info::connection(); - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, propAtom, typeAtom, format, len, data); -} - - -bool XdndWorkaround::clientMessage(xcb_client_message_event_t* event) { - QByteArray event_type = atomName(event->type); - // qDebug() << "client message:" << event_type; - - // NOTE: Because of the limitation of Qt, this hack is required to provide - // Xdnd direct save (XDS) protocol support. - // https://www.freedesktop.org/wiki/Specifications/XDS/#index4h2 - // - // XDS requires that the drop target should get and set the window property of the - // drag source to pass the file path, but in Qt there is NO way to know the - // window ID of the drag source so it's not possible to implement XDS with Qt alone. - // Here is a simple hack. We get the drag source window ID with raw XCB code. - // Then, save it on the drop target widget using QObject dynamic property. - // So in the drop event handler of the target widget, it can obtain the - // window ID of the drag source with QObject::property(). - // This hack works 99.99% of the time, but it's not bullet-proof. - // In theory, there is one corner case for which this will not work. - // That is, when you drag multiple XDS sources at the same time and drop - // all of them on the same widget. (Does XDND support doing this?) - // I do not think that any app at the moment support this. - // Even if somebody is using it, X11 will die and we should solve this in Wayland instead. - // - if(event_type == "XdndDrop") { - // data.l[0] contains the XID of the source window. - // data.l[1] is reserved for future use (flags). - // data.l[2] contains the time stamp for retrieving the data. (new in version 1) - QWidget* target = QWidget::find(event->window); - if(target != nullptr) { // drop on our widget - target = qApp->widgetAt(QCursor::pos()); // get the exact child widget that receives the drop - if(target != nullptr) { - target->setProperty("xdnd::lastDragSource", event->data.data32[0]); - target->setProperty("xdnd::lastDropTime", event->data.data32[2]); - } - } - } - // This part is for Qt >= 5.4 only -#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) - else if(event_type == "XdndFinished") { - lastDrag_ = nullptr; - } -#endif // Qt >= 5.4 - return false; -} - -bool XdndWorkaround::selectionNotify(xcb_selection_notify_event_t* event) { - qDebug() << "selection notify" << atomName(event->selection); - return false; -} - -// This part is for Qt >= 5.4 only -#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) - -bool XdndWorkaround::selectionRequest(xcb_selection_request_event_t* event) { - xcb_connection_t* conn = QX11Info::connection(); - if(event->property == XCB_ATOM_PRIMARY || event->property == XCB_ATOM_SECONDARY) { - return false; // we only touch selection requests related to XDnd - } - QByteArray prop_name = atomName(event->property); - if(prop_name == "CLIPBOARD") { - return false; // we do not touch clipboard, either - } - - xcb_atom_t atomFormat = event->target; - QByteArray type_name = atomName(atomFormat); - // qDebug() << "selection request" << prop_name << type_name; - // We only want to handle text/x-moz-url and text/uri-list - if(type_name == "text/x-moz-url" || type_name.startsWith("text/uri-list")) { - QDragManager* mgr = QDragManager::self(); - QDrag* drag = mgr->object(); - if(drag == nullptr) { - drag = lastDrag_; - } - QMimeData* mime = drag ? drag->mimeData() : nullptr; - if(mime != nullptr && mime->hasUrls()) { - QByteArray data; - QList uris = mime->urls(); - if(type_name == "text/x-moz-url") { - QString mozurl = uris.at(0).toString(QUrl::FullyEncoded); - data.append((const char*)mozurl.utf16(), mozurl.length() * 2); - } - else { // text/uri-list - for(const QUrl& uri : uris) { - data.append(uri.toString(QUrl::FullyEncoded)); - data.append("\r\n"); - } - } - xcb_change_property(conn, XCB_PROP_MODE_REPLACE, event->requestor, event->property, - atomFormat, 8, data.size(), (const void*)data.constData()); - xcb_selection_notify_event_t notify; - notify.response_type = XCB_SELECTION_NOTIFY; - notify.requestor = event->requestor; - notify.selection = event->selection; - notify.time = event->time; - notify.property = event->property; - notify.target = atomFormat; - xcb_window_t proxy_target = event->requestor; - xcb_send_event(conn, false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char*)¬ify); - return true; // stop Qt 5 from touching the event - } - } - return false; // let Qt handle this -} - -bool XdndWorkaround::genericEvent(xcb_ge_generic_event_t* event) { - // check this is an xinput event - if(xinput2Enabled_ && event->extension == xinputOpCode_) { - if(event->event_type == XI_ButtonRelease) { - buttonRelease(); - } - } - return false; -} - -void XdndWorkaround::buttonRelease() { - QDragManager* mgr = QDragManager::self(); - lastDrag_ = mgr->object(); - // qDebug() << "BUTTON RELEASE!!!!" << xcbDrag()->canDrop() << lastDrag_; -} - -#endif // QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) diff --git a/src/xdndworkaround.h b/src/xdndworkaround.h deleted file mode 100644 index 9d40c11..0000000 --- a/src/xdndworkaround.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2016 Hong Jen Yee (PCMan) - * - * 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 - * - */ - -/* - * Note: - * This is a workaround for the following Qt5 bugs. - * - * #49947: Drop events have broken mimeData()->urls() and text/uri-list. - * #47981: Qt5.4 regression: Dropping text/urilist over browser windows stop working. - * - * Related LXQt bug: https://github.com/lxqt/lxqt/issues/688 - * - * This workaround is not 100% reliable, but it should work most of the time. - * In theory, when there are multiple drag and drops at nearly the same time and - * you are using a remote X11 instance via a slow network connection, this workaround - * might break. However, that should be a really rare corner case. - * - * How this fix works: - * 1. Hook QApplication to filter raw X11 events - * 2. Intercept SelectionRequest events sent from XDnd target window. - * 3. Check if the data requested have the type "text/uri-list" or "x-moz-url" - * 4. Bypass the broken Qt5 code and send the mime data to the target with our own code. - * - * The mime data is obtained during the most recent mouse button release event. - * This can be incorrect in some corner cases, but it is still a simple and - * good enough approximation that returns the correct data most of the time. - * Anyway, a workarond is just a workaround. Ask Qt developers to fix their bugs. - */ - -#ifndef XDNDWORKAROUND_H -#define XDNDWORKAROUND_H - -#include - -#include -#include -#include -#include -#include - -class QDrag; - -class XdndWorkaround : public QAbstractNativeEventFilter { -public: - explicit XdndWorkaround(); - ~XdndWorkaround(); - bool nativeEventFilter(const QByteArray& eventType, void* message, long* result) override; - static QByteArray atomName(xcb_atom_t atom); - static xcb_atom_t internAtom(const char* name, int len = -1); - static QByteArray windowProperty(xcb_window_t window, xcb_atom_t propAtom, xcb_atom_t typeAtom, int len); - static void setWindowProperty(xcb_window_t window, xcb_atom_t propAtom, xcb_atom_t typeAtom, void* data, int len, int format = 8); - -private: - bool clientMessage(xcb_client_message_event_t* event); - bool selectionNotify(xcb_selection_notify_event_t* event); - -// This part is for Qt >= 5.4 only -#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) -private: - bool selectionRequest(xcb_selection_request_event_t* event); - bool genericEvent(xcb_ge_generic_event_t* event); - // _QBasicDrag* xcbDrag() const; - void buttonRelease(); - - QPointer lastDrag_; - // xinput related - bool xinput2Enabled_; - int xinputOpCode_; - int xinputEventBase_; - int xinputErrorBase_; -#endif // Qt >= 5.4 -}; - -#endif // XDNDWORKAROUND_H