Import libfm-qt_0.14.0.orig.tar.xz
authorAlf Gaida <agaida@siduction.org>
Wed, 30 Jan 2019 19:32:59 +0000 (19:32 +0000)
committerAlf Gaida <agaida@siduction.org>
Wed, 30 Jan 2019 19:32:59 +0000 (19:32 +0000)
[dgit import orig libfm-qt_0.14.0.orig.tar.xz]

234 files changed:
AUTHORS [new file with mode: 0644]
CHANGELOG [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
Doxyfile.in [new file with mode: 0644]
LICENSE [new file with mode: 0644]
README.md [new file with mode: 0644]
cmake/fm-qt-config.cmake.in [new file with mode: 0644]
data/CMakeLists.txt [new file with mode: 0644]
data/archivers.list [new file with mode: 0644]
data/libfm-qt-mimetypes.xml [new file with mode: 0644]
data/terminals.list [new file with mode: 0644]
src/CMakeLists.txt [new file with mode: 0644]
src/app-chooser-dialog.ui [new file with mode: 0644]
src/appchoosercombobox.cpp [new file with mode: 0644]
src/appchoosercombobox.h [new file with mode: 0644]
src/appchooserdialog.cpp [new file with mode: 0644]
src/appchooserdialog.h [new file with mode: 0644]
src/applaunchcontext.cpp [new file with mode: 0644]
src/applaunchcontext.h [new file with mode: 0644]
src/appmenuview.cpp [new file with mode: 0644]
src/appmenuview.h [new file with mode: 0644]
src/appmenuview_p.h [new file with mode: 0644]
src/bookmarkaction.cpp [new file with mode: 0644]
src/bookmarkaction.h [new file with mode: 0644]
src/browsehistory.cpp [new file with mode: 0644]
src/browsehistory.h [new file with mode: 0644]
src/cachedfoldermodel.cpp [new file with mode: 0644]
src/cachedfoldermodel.h [new file with mode: 0644]
src/colorbutton.cpp [new file with mode: 0644]
src/colorbutton.h [new file with mode: 0644]
src/core/archiver.cpp [new file with mode: 0644]
src/core/archiver.h [new file with mode: 0644]
src/core/basicfilelauncher.cpp [new file with mode: 0644]
src/core/basicfilelauncher.h [new file with mode: 0644]
src/core/bookmarks.cpp [new file with mode: 0644]
src/core/bookmarks.h [new file with mode: 0644]
src/core/cstrptr.h [new file with mode: 0644]
src/core/deletejob.cpp [new file with mode: 0644]
src/core/deletejob.h [new file with mode: 0644]
src/core/dirlistjob.cpp [new file with mode: 0644]
src/core/dirlistjob.h [new file with mode: 0644]
src/core/filechangeattrjob.cpp [new file with mode: 0644]
src/core/filechangeattrjob.h [new file with mode: 0644]
src/core/fileinfo.cpp [new file with mode: 0644]
src/core/fileinfo.h [new file with mode: 0644]
src/core/fileinfo_p.h [new file with mode: 0644]
src/core/fileinfojob.cpp [new file with mode: 0644]
src/core/fileinfojob.h [new file with mode: 0644]
src/core/filelinkjob.cpp [new file with mode: 0644]
src/core/filelinkjob.h [new file with mode: 0644]
src/core/filemonitor.cpp [new file with mode: 0644]
src/core/filemonitor.h [new file with mode: 0644]
src/core/fileoperationjob.cpp [new file with mode: 0644]
src/core/fileoperationjob.h [new file with mode: 0644]
src/core/filepath.cpp [new file with mode: 0644]
src/core/filepath.h [new file with mode: 0644]
src/core/filesysteminfojob.cpp [new file with mode: 0644]
src/core/filesysteminfojob.h [new file with mode: 0644]
src/core/filetransferjob.cpp [new file with mode: 0644]
src/core/filetransferjob.h [new file with mode: 0644]
src/core/folder.cpp [new file with mode: 0644]
src/core/folder.h [new file with mode: 0644]
src/core/folderconfig.cpp [new file with mode: 0644]
src/core/folderconfig.h [new file with mode: 0644]
src/core/gioptrs.h [new file with mode: 0644]
src/core/gobjectptr.h [new file with mode: 0644]
src/core/iconinfo.cpp [new file with mode: 0644]
src/core/iconinfo.h [new file with mode: 0644]
src/core/iconinfo_p.h [new file with mode: 0644]
src/core/job.cpp [new file with mode: 0644]
src/core/job.h [new file with mode: 0644]
src/core/job_p.h [new file with mode: 0644]
src/core/legacy/fm-app-info.c [new file with mode: 0644]
src/core/legacy/fm-app-info.h [new file with mode: 0644]
src/core/legacy/fm-config.c [new file with mode: 0644]
src/core/legacy/fm-config.h [new file with mode: 0644]
src/core/legacy/glib-compat.h [new file with mode: 0644]
src/core/mimetype.cpp [new file with mode: 0644]
src/core/mimetype.h [new file with mode: 0644]
src/core/templates.cpp [new file with mode: 0644]
src/core/templates.h [new file with mode: 0644]
src/core/terminal.cpp [new file with mode: 0644]
src/core/terminal.h [new file with mode: 0644]
src/core/thumbnailer.cpp [new file with mode: 0644]
src/core/thumbnailer.h [new file with mode: 0644]
src/core/thumbnailjob.cpp [new file with mode: 0644]
src/core/thumbnailjob.h [new file with mode: 0644]
src/core/totalsizejob.cpp [new file with mode: 0644]
src/core/totalsizejob.h [new file with mode: 0644]
src/core/trashjob.cpp [new file with mode: 0644]
src/core/trashjob.h [new file with mode: 0644]
src/core/untrashjob.cpp [new file with mode: 0644]
src/core/untrashjob.h [new file with mode: 0644]
src/core/userinfocache.cpp [new file with mode: 0644]
src/core/userinfocache.h [new file with mode: 0644]
src/core/vfs/fm-file.c [new file with mode: 0644]
src/core/vfs/fm-file.h [new file with mode: 0644]
src/core/vfs/fm-xml-file.c [new file with mode: 0644]
src/core/vfs/fm-xml-file.h [new file with mode: 0644]
src/core/vfs/vfs-menu.c [new file with mode: 0644]
src/core/vfs/vfs-search.c [new file with mode: 0644]
src/core/volumemanager.cpp [new file with mode: 0644]
src/core/volumemanager.h [new file with mode: 0644]
src/createnewmenu.cpp [new file with mode: 0644]
src/createnewmenu.h [new file with mode: 0644]
src/customaction_p.h [new file with mode: 0644]
src/customactions/fileaction.cpp [new file with mode: 0644]
src/customactions/fileaction.h [new file with mode: 0644]
src/customactions/fileactioncondition.cpp [new file with mode: 0644]
src/customactions/fileactioncondition.h [new file with mode: 0644]
src/customactions/fileactionprofile.cpp [new file with mode: 0644]
src/customactions/fileactionprofile.h [new file with mode: 0644]
src/dirtreemodel.cpp [new file with mode: 0644]
src/dirtreemodel.h [new file with mode: 0644]
src/dirtreemodelitem.cpp [new file with mode: 0644]
src/dirtreemodelitem.h [new file with mode: 0644]
src/dirtreeview.cpp [new file with mode: 0644]
src/dirtreeview.h [new file with mode: 0644]
src/dndactionmenu.cpp [new file with mode: 0644]
src/dndactionmenu.h [new file with mode: 0644]
src/dnddest.cpp [new file with mode: 0644]
src/dnddest.h [new file with mode: 0644]
src/edit-bookmarks.ui [new file with mode: 0644]
src/editbookmarksdialog.cpp [new file with mode: 0644]
src/editbookmarksdialog.h [new file with mode: 0644]
src/exec-file.ui [new file with mode: 0644]
src/execfiledialog.cpp [new file with mode: 0644]
src/execfiledialog_p.h [new file with mode: 0644]
src/file-operation-dialog.ui [new file with mode: 0644]
src/file-props.ui [new file with mode: 0644]
src/filedialog.cpp [new file with mode: 0644]
src/filedialog.h [new file with mode: 0644]
src/filedialog.ui [new file with mode: 0644]
src/filedialoghelper.cpp [new file with mode: 0644]
src/filedialoghelper.h [new file with mode: 0644]
src/filelauncher.cpp [new file with mode: 0644]
src/filelauncher.h [new file with mode: 0644]
src/filemenu.cpp [new file with mode: 0644]
src/filemenu.h [new file with mode: 0644]
src/filemenu_p.h [new file with mode: 0644]
src/fileoperation.cpp [new file with mode: 0644]
src/fileoperation.h [new file with mode: 0644]
src/fileoperationdialog.cpp [new file with mode: 0644]
src/fileoperationdialog.h [new file with mode: 0644]
src/fileoperationdialog_p.h [new file with mode: 0644]
src/filepropsdialog.cpp [new file with mode: 0644]
src/filepropsdialog.h [new file with mode: 0644]
src/filesearch.ui [new file with mode: 0644]
src/filesearchdialog.cpp [new file with mode: 0644]
src/filesearchdialog.h [new file with mode: 0644]
src/fm-search.c [new file with mode: 0644]
src/fm-search.h [new file with mode: 0644]
src/folderitemdelegate.cpp [new file with mode: 0644]
src/folderitemdelegate.h [new file with mode: 0644]
src/foldermenu.cpp [new file with mode: 0644]
src/foldermenu.h [new file with mode: 0644]
src/foldermodel.cpp [new file with mode: 0644]
src/foldermodel.h [new file with mode: 0644]
src/foldermodelitem.cpp [new file with mode: 0644]
src/foldermodelitem.h [new file with mode: 0644]
src/folderview.cpp [new file with mode: 0644]
src/folderview.h [new file with mode: 0644]
src/folderview_p.h [new file with mode: 0644]
src/fontbutton.cpp [new file with mode: 0644]
src/fontbutton.h [new file with mode: 0644]
src/libfm-qt.pc.in [new file with mode: 0644]
src/libfmqt.cpp [new file with mode: 0644]
src/libfmqt.h [new file with mode: 0644]
src/libfmqtglobals.h [new file with mode: 0644]
src/mount-operation-password.ui [new file with mode: 0644]
src/mountoperation.cpp [new file with mode: 0644]
src/mountoperation.h [new file with mode: 0644]
src/mountoperationpassworddialog.cpp [new file with mode: 0644]
src/mountoperationpassworddialog_p.h [new file with mode: 0644]
src/mountoperationquestiondialog.cpp [new file with mode: 0644]
src/mountoperationquestiondialog_p.h [new file with mode: 0644]
src/pathbar.cpp [new file with mode: 0644]
src/pathbar.h [new file with mode: 0644]
src/pathbar_p.h [new file with mode: 0644]
src/pathedit.cpp [new file with mode: 0644]
src/pathedit.h [new file with mode: 0644]
src/pathedit_p.h [new file with mode: 0644]
src/placesmodel.cpp [new file with mode: 0644]
src/placesmodel.h [new file with mode: 0644]
src/placesmodelitem.cpp [new file with mode: 0644]
src/placesmodelitem.h [new file with mode: 0644]
src/placesview.cpp [new file with mode: 0644]
src/placesview.h [new file with mode: 0644]
src/proxyfoldermodel.cpp [new file with mode: 0644]
src/proxyfoldermodel.h [new file with mode: 0644]
src/rename-dialog.ui [new file with mode: 0644]
src/renamedialog.cpp [new file with mode: 0644]
src/renamedialog.h [new file with mode: 0644]
src/sidepane.cpp [new file with mode: 0644]
src/sidepane.h [new file with mode: 0644]
src/tests/test-filedialog.cpp [new file with mode: 0644]
src/tests/test-folder.cpp [new file with mode: 0644]
src/tests/test-folderview.cpp [new file with mode: 0644]
src/tests/test-placesview.cpp [new file with mode: 0644]
src/tests/test-volumemanager.cpp [new file with mode: 0644]
src/translations/CMakeLists.txt [new file with mode: 0644]
src/translations/libfm-qt.ts [new file with mode: 0644]
src/translations/libfm-qt_ar.ts [new file with mode: 0644]
src/translations/libfm-qt_ca.ts [new file with mode: 0644]
src/translations/libfm-qt_cs.ts [new file with mode: 0644]
src/translations/libfm-qt_cy.ts [new file with mode: 0644]
src/translations/libfm-qt_da.ts [new file with mode: 0644]
src/translations/libfm-qt_de.ts [new file with mode: 0644]
src/translations/libfm-qt_el.ts [new file with mode: 0644]
src/translations/libfm-qt_es.ts [new file with mode: 0644]
src/translations/libfm-qt_fr.ts [new file with mode: 0644]
src/translations/libfm-qt_gl.ts [new file with mode: 0644]
src/translations/libfm-qt_he.ts [new file with mode: 0644]
src/translations/libfm-qt_hu.ts [new file with mode: 0644]
src/translations/libfm-qt_id.ts [new file with mode: 0644]
src/translations/libfm-qt_it.ts [new file with mode: 0644]
src/translations/libfm-qt_ja.ts [new file with mode: 0644]
src/translations/libfm-qt_lt.ts [new file with mode: 0644]
src/translations/libfm-qt_nb_NO.ts [new file with mode: 0644]
src/translations/libfm-qt_nl.ts [new file with mode: 0644]
src/translations/libfm-qt_pl.ts [new file with mode: 0644]
src/translations/libfm-qt_pt.ts [new file with mode: 0644]
src/translations/libfm-qt_pt_BR.ts [new file with mode: 0644]
src/translations/libfm-qt_ru.ts [new file with mode: 0644]
src/translations/libfm-qt_tr.ts [new file with mode: 0644]
src/translations/libfm-qt_uk.ts [new file with mode: 0644]
src/translations/libfm-qt_zh_CN.ts [new file with mode: 0644]
src/translations/libfm-qt_zh_TW.ts [new file with mode: 0644]
src/utilities.cpp [new file with mode: 0644]
src/utilities.h [new file with mode: 0644]
src/utilities_p.h [new file with mode: 0644]
src/utils.h [new file with mode: 0644]
src/xdndworkaround.cpp [new file with mode: 0644]
src/xdndworkaround.h [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..a95b73b
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,6 @@
+Upstream Authors:
+    LXQt team: https://lxqt.org
+    Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+
+Copyright:
+    Copyright (c) 2013-2018 LXQt team
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644 (file)
index 0000000..1a9a80f
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,715 @@
+libfm-qt-0.14.0 / 2019-01-25
+============================
+
+ * Completely dropped libfm dependency.
+ * An option for showing full names instead of display names.
+ * An option for shadowing hidden icons.
+ * Fixed handling of mounts and SMB.
+ * Fixed overwrite prompt in LXQt file dialog.
+ * Fixed rare showing of nonexistent files.
+ * Moved Qt file dialog helper into libfm-qt.
+ * Support adding metadata for trusting executables and add an emblem to untrusted desktop files inside home.
+ * Fixed a problem in selecting newly created files.
+ * Fixed a random crash by adding context to folder lambda connections of file dialog.
+ * Selection/deselection corner marks on mouse-over.
+ * Ensure visibility of toggled path button on resizing window.
+ * Use target URI for symlink thumbnails.
+ * Return the correct path in "FileInfo::path()" by giving the path itself to the ctor of "FileInfo" wherever possible.
+ * Fixed an infinite loop on trashing a file that couldn't be trashed.
+ * Added dir file number to Properties dialog.
+ * Added device usge bar to Properties dialog.
+ * Volume tooltips on side-pane.
+ * Fix selection on file creation in detailed list view.
+ * Removed senseless "Canceled Operation" messages.
+ * Let items be selected by typing in detaled list view too.
+ * Made path-edit completer insensitive to case.
+ * Allow compressing a selection of different mimetypes in the context menu.
+ * Span first column of Places pane (not for Devices), so that names aren't elided unnecessarily.
+ * Fixed randomly missing eject button with mounting.
+ * Don't launch unselected current item on pressing Enter.
+ * Don't update trash icon too often.
+ * Fixed crash on launching desktop files with empty Exec.
+ * Fixes for template actions (showing them sorted).
+ * Prevent a directory from being copied into itself (or into a subdir of itself); show an error message instead.
+ * Fixed a problem in deletion queues -- while monitoring a directory -- by preventing file info jobs from overlapping each other.
+ * Allow item activation by pressing ENTER when the cursor is over its selection corner.
+ * Fixed mimetype filters in LXQt file dialog.
+ * Fixed view icons with HDPI scaling.
+
+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
+
+libfm-qt-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<const FileInfo> since it's so frequently used.
+  * Correctly handle the mounting of mountable paths and correctly parse the target URIs of shortcuts.
+  * Add new APIs Fm::MountOperation::mountEnclosingVolume() and Fm::MountOperation::mountMountable() and deprecate Fm::MountOperation::mount().
+  * Also cover local shortcuts
+  * Added "reject" to exec dialog
+  * Delete some libfm compatibility wrappers.
+  * Delete the unused old FmTemplate wrapper.
+  * Create the template items in the "Create New" menu with the new C++ file template APIs.
+  * Port template support to C++ (Fm::Templates class).
+  * Add convinient functions in Fm::FileOperation to set dest paths for file transfer jobs.
+  * Made job async again
+  * Fixed launching of desktop files
+  * Cleanup
+  * CMake: Prevent in-source builds
+  * Fix Fm::FileInfo::isDir() and isExecutableType() to make its behavior consistent with libfm.
+  * Port file lancher to C++ and Implement Fm::BasicFileLauncher base class. * Migrate Fm::FileLauncher to the new C++ implementation.
+  * Port archiver integration to C++ (#182)
+  * Fully port all file operations to C++ 11 (#181)
+  * Add kitty to terminals.list
+  * Italic font for hidden items
+  * Select multiple files
+  * No visible cursor in File Properties labels
+  * Just corrected a misspelling
+  * Elided labels for file operation dialog
+  * fix namespace, fixes lxqt/libfm-qt/issues/174
+  * Misc link fixes
+  * Fixes some pathes after repo move
+  * Fix a cause of crash in `AppChooserComboBox`
+  * build: Bump version
+  * Drop Fm::IconTheme
+  * iconinfo: Properly handle multiple names
+  * Some code cleanup
+  * Fixed custom action execution mode
+  * Don't drop on files
+  * Prevent possible c++11 range-loop container detach
+  * See `.bak` and `.old` as backup extensions; also follow GLib
+  * Ensure that rename editor has opaque background
+  * Use special/custom folder icons for bookmarks
+  * Added two missing cases of `mapFromSource()`
+  * Support hiding items in Places side-pane
+  * Fixed sorting by type/owner
+  * Fix lambda connections in filedialog (#159)
+  * Drop Q_FOREACH
+  * Fix memory leak in thumbnail loading (#150)
+  * Be more tolerant
+  * Fix the "Folders" key in custom actions
+  * Add Group column and don't use owner's full name
+  * Fix comparison of integers of different signs
+  * Guarantee 64-bit time attributes (#148)
+  * Added a proxy setting for backup as hidden (#145)
+  * Fixed the logic of queued deletion
+  * Track folders containing cut files only with QString
+  * Copy selected pathbar text to selection clipboard
+  * Smooth scrolling for icon and thumbnail views
+  * cmake: Handle CMP0071
+  * Prepare libfm-qt for bulk rename
+  * No change queue of files in the deletion queue
+  * FileInfo: Fix potential SEGFAULT
+  * Fix two devices for one mount
+  * Always wait for the folder to load before selecting
+  * Really cancel multiple renaming on cancelling
+  * Prevent a potential crash in xdndworkaround
+  * Fix wrong gray-out of cut files
+  * Merge side-pane with its surroundings
+  * Prompt dialog specifically for desktop files
+  * Remove unnecessary noise
+
+libfm-qt-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.
+
+libfm-qt-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)
+
+libfm-qt-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
+
+libfm-qt-0.11.0 / 2016-02-27
+============================
+
+  * Bump version number and ABI version and preparing for the initial release.
+  * Redirect focus operation on the folderview to the actual widget
+  * Use grid layout to have proper properties alignment
+  * Focus automatically on the first field of the filesearch dialog
+  * Add (folder) custom actions to foldermenu.
+  * Update czech translation (by Petr Balíček <pbalicek@seznam.cz>)
+  * Fix compiling with Qt < 5.4
+  * Add supports for freedesktop Xdnd direct save (XDS) protocol (closes #pcmanfm-qt/298). * Move Xdnd workarounds from pcmanfm-qt to libfm-qt.
+  * Protected FolderView methods for getting and setting item delegate margins
+  * Perform auto-completion for the path bar when the user press Tab key. This closes pcmanfm-qt/#201.
+  * Disable unused options in file search dialog
+  * Let the style engine draw the text selection rectangle.
+  * Fix file search with smaller than option
+  * Fix target language in Lithuanian translation file
+  * Fix memory leak if reading stream for image failed
+  * Update German translation
+  * Polish translation updated
+  * Polish translation updated
+  * Add a missing type casting to fix a compiler warning.
+  * Relicense libfm-qt to LGPL v.2.1.
+  * Avoid using blocking calls when initializing the trash can to speed up startup.
+  * Updated Russian translation Removed ru_RU file
+  * Create DnD menu with supported actions only
+  * make createAction public so can be hidden from view
+  * Adds Runtime and Devel install COMPONENT
+  * Quote everything that could break due to whitespaces
+  * Use CMake GenerateExportHeader to generate ABI header file
+  * Use no exceptions also with Clang
+  * Uses CMAKE_VISIBILITY and CMAKE_CXX_VISIBILITY flags
+  * Use QString() instead of ""
+  * Fix duplicated mtp mounts in the side pane by adding workarounds for potential bugs of gvfs.
+  * Replace g_io_scheduler which is deprecated with QThread in Fm::PathEdit. * Fix compiler warnings.
+  * Adds .gitignore
+  * Makes Release the default BUILD_TYPE
+  * Adds INSTALL package configuration file
+  * Removes XCB from the dependencies
+  * Adds target_include_directories() for the INSTALL_INTERFACE.
+  * Removes CMAKE_CURRENT_BINARY_DIR usage
+  * Removes QTX_INCLUDE_DIRS
+  * Removes Qt Creator .user file
+  * Updates libraries dependencies
+  * Adds REQUIRED_PACKAGE_VERSION variables
+  * Adds generation of TARGETS CMake file
+  * Creates and installs an package version file
+  * Renames and moves LIBRARY_NAME variable to the top CMakeLists
+  * Removes sub-project
+  * Rename the library to libfm-qt and fix installed header files.
+  * Split pcmanfm-qt into two parts and move libfm-qt to its own repository.
+  * Update French translation
+  * Italian translation updates
+  * Fix a crash triggered when unmounting a volume from the side pane.
+  * Avoid duplicated volumes and mounts in the side panel. (This fixes the mtp:// problem.)
+  * Fix missing null pointer check in Fm::Path::parent() and use nullptr instead of NULL in path.h.
+  * Code cleanup, «using» directive only if necessary
+  * Upgrade of pcmanfm-qt to C++11
+  * hu translations updated
+  * Fixed several problems with item selection and alignment
+  * Remove unnecessary qDebug traces
+  * Update translations.
+  * The signal QAbstractItemView::iconSizeChanged is only available after Qt 5.5. Add workarounds for older Qt versions.
+  * Add more null pointer checks in the thumbnail loader to avoid crashes caused by older versions of libfm.
+  * Update translations
+  * Avoid the column resizing tricks for the detailed list view when the view contains no columns.
+  * Improve the column width computation for the detailed view
+  * PlacesView: activate on click on the second column
+  * SidePane: reduce size of button's column width
+  * Added a filterbar + Handle virtually hidden files
+  * Russian translation update
+  * Update cs_CZ translation with the strings provided by petrbal in pull request #218.
+  * Allow adding or removing paths in the file search dialog. Fix bugs in searching for documents.
+  * Try to implement file searching by using the new Fm::FileSearchDialog class.
+  * Fix a incorrecy free() in fm_search_free() API.
+  * Add Fm::Path::take() API to take the ownership of a raw FmPath pointer.
+  * Add class Fm::FileSearchDialog used to show a dialog for searching files.
+  * Add FmSearch API which is used to build a search:// URI. (implemented in C and might become part of libfm later).
+  * Fix #195 - Crash when rightclick on item in trash.
+  * Add a null check for FmFileInfo in Fm::ProxyFolderModel::lessThan(). This closes #205.
+  * Fix (workaround) for right-click crash in placesview.
+  * Russian translation: update
+  * Italian translation: add desktop entry files, adjust TS files
+  * placesview: middle-click correct item to activate (fix of segfault)
+  * Check for null pointers.
+  * Select the folder from where we have gone up.
+  * Paste into folder from its context menu.
+  * libfm-qt: updated german translation
+  * libfm-qt: lupdated translation files
+  * Add Greek (el) translation
+  * added support for mouse back/forward buttons
+  * Update German translation
+  * Add new signal prepareFileMenu() to Fm::SidePane and Fm::DirTree so there's  a chance to customize the file menu before its shown.
+  * Port some missing config options from the gtk+ version of pcmanfm.
+  * Also show hidden dirs in the directory tree when the "Show Hidden" option in the menu is turned on.
+  * Fix #190 - Column layout is not always updated.
+  * Create New menu actions, context menu in tree side pane, #163.
+  * Store side pane mode setting, #157.
+  * Fixes an translation regression
+  * Updates translations
+  * Uses LXQt lxqt_translate_ts() to handle translations
+  * Fix lxde/lxqt#447 - missing actions in Places' context menus
+  * Remove trailing whitespaces
+  * polishing German translation
+  * Add menu items and shortcuts for new folder and blank file, fixes #163.
+  * Display folders first when active and sort order descending, fixes #179.
+  * Avoid space wasted by incorrect decoration in detailed view columns, fixes #177.
+  * Avoid flickering column header while resizing manually, fixes #176.
+  * Hungarian translation
+  * Fix #627 - long startup time. (This blocking is caused by checking the availability of "network:///".)
+  * Enable text selection in file properties dialog
+  * Fix some memory leaks reported by valgrind.
+  * Fix warnings reported by cppcheck.
+  * Fix warnings reported by scan-build.
+  * Sort indicators in detailed view, store column and order in settings, fixes #109.
+  * Fix lxde/lxqt#512 - pcmanfm-qt: cannot delete to trash.
+  * Polish translations added
+  * Use 'user-trash' icon for 'Move to Trash'
+  * The "Custom" option in the application chooser combo box inside file properties dialog is broken. Fix by preventing recursive signal handler invocation.
+  * The file property dialog does not show correct default applications. Fix a bug in AppChooserComboBox::setMimeType() that does incorrect app comparison.
+  * When converting an UID or GID to its name, show the number as string when the user or group does not exists.
+  * Add more null checks.
+  * Portuguese update
+  * Add very basic "remaining time" display to the progress dialog. Fix lxde/lxqt#463 - Progress dialog of pcmanfm-qt does not show remaining time.
+  * Fix lxde/pcmanfm-qt#120 - Foucs "Rename" button when file name changed.
+  * Remove unnecessary '\n' charactor from the translated strings.
+  * Fix translations (the newly added string comes from the translation of libfm).
+  * Improve trash can handling: provide an option to delete the files if moving to trashcan fails.
+  * Fix broken filenames of translation files.
+  * More migration to Qt5 new signal/slot syntax for better type safety & speed.
+  * Migrade to new Qt5 signal/slot syntax for better type safety and speed.
+  * Fix the broken sorting option.
+  * Fix lxde/lxqt#448 - PCmanFM-QT renaming place bookmarks does nothing.
+  * Support linguistic sorting of file names. This fixes #105.
+  * Update the folder model & view after files are changed.
+  * Open folders in new tabs by middle clicking on items in the side pane.
+  * Portuguese update
+  * Fix a crash of the context menu of places view caused by change of items.
+  * Save the result of "Edit bookmarks" to gtk+3 bookmarks file instead of the deprecated ~/.gtkbookmarks file. This fixes bug #112 partially.
+  * Add spanish translations
+  * Update Japanese translation
+  * Add German translation
+  * add Japanese translation
+  * Implement "UnTrash" for files in trash:/// and close lxde/lxqt#136.
+  * Add Russian translation
+  * Drop Qt4 support in code
+  * Clean up CMakeLists.txt and drop Qt4 support
+  * New files added from LXDE Pootle server based on templates
+  * Commit from LXDE Pootle server by user Julius22.: 1007 of 1008 strings translated (2 need review).
+  * Commit from LXDE Pootle server by user Julius22.: 709 of 1008 strings translated (2 need review).
+  * Commit from LXDE Pootle server by user mbouzada.: 364 of 364 strings translated (0 need review).
+  * New files added from LXDE Pootle server based on templates
+  * Add cs_CZ translation for libfm-qt.
+  * Commit from LXDE Pootle server by user dforsi.: 364 of 364 strings translated (0 need review).
+  * Commit from LXDE Pootle server by user dforsi.: 358 of 364 strings translated (0 need review).
+  * Bump package version number and library soversion to prepare for 0.2 release.
+  * Fix #85 - Scrolling doesn't work in compact view.
+  * Hide UI elements that are not usable and disable trash can when gvfs is not available. * Add new API Fm::isUriSchemeSupported().
+  * Avoid showing the popup menu when moving desktop items.
+  * Improve handling of file selection and fixing FolderView::selectAll(), which is reported in #45. Delay the handling of selectionChanged() signal to avoid too frequent UI updates.
+  * Little adjustment for the grid of the folder view to decrease unnecessary margins.
+  * Use a new way to optimize the size of filename display based on current view mode and font size. This also fixes lxde/lxde-qt #198 - PCmanFM-qt incorrectly displays 256x256 Thumbnails.
+  * Fully support single click activation and auto-selection with associated options added to the preference dialog.
+  * Add single click and auto-selection on hover support to Fm::FolderView.
+  * New files added from LXDE Pootle server based on templates
+  * New files added from LXDE Pootle server based on templates
+  * Improve update of translations to avoid unnecessary regeneration of ts files.
+  * Improve handling of fallback icons. This closes bug #57.
+  * Translations are lost accidentally in a previous commit. Restore them all.
+  * Fix a crash in Fm::PlacesModel when gvfs is not available. This closes bug #35 - Ctrl+W closes all windows.
+  * Do not detect filename extension and select the whole filename by default while renaming directories. This closes bug #71 - Don't try to detect extensions on directories. * API changed: Fm::renameFile() now accepect FmFileInfo as its first parameter.
+  * Fix bug #80 - make execute in context menu doesn't do change permissions.
+  * Revert "fixed selection issue #45" This patch breaks copying files by DND in icon view mode and moving desktop icons.
+  * Use qss instead of QPalette to set the background color of ColorButton. This fixed bug #192 of lxde-qt.
+  * Rename the library from libfm-qt to libfm-qt5 when built with Qt5.
+  * fixed selection issue #45
+  * Fix middle click position calculation in detailed view mode
+  * Fix crash when context menu is requested but selection is empty
+  * Activate view items only if clicked with left mouse button
+  * Do not emit activated signal when keyboard modifiers are on.
+  * Splits the checks for needed libraries
+  * Removes duplicated include_directories() entry
+  * Make sure clang compiler does not complain
+  * Install pkgconfig file of libfm-qt to correct location in FreeBSD
+  * Fix missing return values in several methods.
+  * Avoid endless popups of error dialogs when there are errors launching files.
+  * Save thumbnails as png files correctly.
+  * Remember custom positions for desktop icons and fix #29.
+  * Add template support to the folder context menus and fix #39.
+  * Show "owner" in the detailed list view mode. * Fix a crash when switching to detailed list mode in qt5.
+  * Use xcb to set EWMH window type hint to the desktop window in Qt5. * Some more cleanup for the CMakeList.txt files
+  * Add initial support for Qt5.
+  * Try to fix #36 again.
+  * Fix a seg fault caused by the widget being deleted during glib signal handling.
+  * Code cleanup, removing unnecessary header inclusion to speed up compilation.
+  * Avoid further handling of MountOperation in the gio finished callback if the object is deleted.
+  * Use modeless dialogs for app chooser and error reporting in Fm::FileLauncher and Fm::FileMenu.
+  * Add an small emblem for symlinks (using icon name "emblem-symbolic-link"). Fix bug #27.
+  * Add missing file to git.
+  * Move internal implementation details to private headers which are not installed to the system.
+  * Implement Fm::AppChooserDialog and Fm::AppMenuView classes. * Add <Open with...>/<Other Applications> menu item to Fm::FileMenu. * Add custom app to Fm::AppChooserComboBox.
+  * Add Fm::AppChooserComboBox and use it in Fm::FilePropsDialog.
+  * Redesign Fm::FileLauncher APIs to make it more usable. * Add Fm::FileMenu::setFileLauncher() and Fm::FolderView::setFileLauncher() APIs. * Move PCManFM::View::onFileClick() and other popup menu handling to Fm::FolderView.
+  * Improve Fm::FileLaucher to make it easy to derive subclasses. * Implement a special dialog for opening executable files (Fix bug #13 - it does not launch executables)
+  * Fix bug #28 - Tash can icon does not refresh when the trash can changes its empty/full status
+  * Load autocompletion list for Fm::PathEdit only when the widget has the keyboard focus to avoid unnecessary I/O.
+  * Add proper popup menu items for selected folders and fix #20 and #19. * Some code refactors, adding openFolders() and openFolderInTerminal() to Application class.
+  * Fix #25 - Amount of items in the folder is not refreshed when the folder content changes. * Update status bar text properly on switching tab pages, selection changes, and folder content changes.
+  * Fix the broken compiler definitions caused by previous commit.
+  * Fix bug #22 - Do not select file extension by default on file rename. * Avoid installing private headers (*_p.h files)
+  * Setup pcmanfm-qt to support optional Custom Actions Menubar detects if libfm was built with vala or not if so a fm-actions.h will exist and support will be compiled in if not, will still compile with no actions menu
+  * Allow installation path configuration with standard CMake X_INSTALL_DIR
+  * Support reordering bookmark items in the places view with DND.
+  * Support adding bookmarks to the places view using drag and drop
+  * Preparing for implementing dnd for places view.
+  * Improve the usability of icon view mode, fixing github bug #24.
+  * Fix crashes caused by invalid pointer access.
+  * Switch current dir of the folder view correctly with dir tree view in the side pane.
+  * Finish chdir operation for Fm::DirTreeView.
+  * Support hiding hidden folders from DirTreeModel.
+  * Move some methods from DirTreeModel to DirTreeModelItem and fix some row updating problems.
+  * Implement dynamic folder loading/unloading when expanding or collapsing dir tree nodes. * Enable horizontal scrollbar of dir tree view.
+  * Move some code from Fm::DirTreeModel to Fm::DirTreeModelItem.
+  * Partially implement Fm::DirTreeView and Fm::DirTreeModel. (not finished yet)
+  * Fix an invalid pointer
+  * Implment different modes for Fm::SidePane, matching libfm-qtk design. * Add basic skeleton for dir tree view/model.
+  * Fix the cosmetic defect introduced by the eject buttons in the places view.
+  * Add eject buttons to mounted volumes in the places side pane.
+  * Add a wrapper class Fm::Path for FmPath C struct.
+  * Initialize icon_ member of PlacesModelItem correctly.
+  * Fix fallback icon when a platform plugin is abscent. * Make Fm::IconTheme::checkUpdate() a static function.
+  * Remove xsettings support. Use a cleaner way to detect config changes by monitor StyleChange events. * Add Fm::IconTheme::changed() signal which is emitted when the icon theme name is changed. * Replace nested Fm::PlacesModel::Item and related classes with their own separate toplevel classes.
+  * Fix the icon for files of unknown mime types, again.
+  * Fix the icon for files of unknown mime types.
+  * Add DES-EMA custom actions to the popup menus.
+  * Make it safe to create multiple Fm::LibFmQt objects and only initialize libfm once.
+  * Fix incorrect export symbols and use GNUInstallDirs for installation destination
+  * Use the latest libfm thumbnailer APIs.
+  * Fix #3614873 - Thumbnails in icon view shown upside down for some jpegs.
+  * Adopt recent changes of libfm. FmIcon is essentially identical to GIcon now.
+  * Add a UI file for application chooser dialog.
+  * Correctly handle display names of folders in path entry auto-completion.
+  * Add a global header and add proper definition for LIBFM_QT_API macro.
+  * Add "Empty trash" and fix a memory leak.
+  * Fix memory leaks for bookmarks. Fix the broken "Network" item in places.
+  * Reduce memory usage: Paint the folder items with our own code instead of using a dirty hacks duplicating pixmaps.
+  * Reduce of size of QPixmapCache in the hope of decreasing memory usage.
+  * Add fallback icons for places item "applications" and "network".
+  * Add class Fm::CachedFolderModel, a convinient way to share Fm::FolderModel objects and reduce memory usage.
+  * Resize the columns of detailed list view when items are inserted or removed.
+  * Optimize column widths in detailed list mode when the view is resized.
+  * Only show thumbnails for the first column in detailed list mode.
+  * Use new "automoc" feature of cmake 2.8.6 and remove cumbersome #include "*.moc" code.
+  * Trivial fix.
+  * Add additional custom filter support to ProxyFolderModel.
+  * Fix some memory leaks.
+  * Fix some compiler errors and update translations.
+  * Support the latest libfm trunk. Remove -fpermissive compiler flag and fix compiler errors/warnings.
+  * Adopt new libfm thumbnail APIs.
+  * Add soname 0.0.0 for libfm-qt, preparing for 0.1 release.
+  * Fix crashes caused by incorrect deletion of dialog objects.
+  * Enable thumbnail related settings.
+  * Update zh_TW translations and translation templates.
+  * Add Portuguese translation (pt).
+  * Add Lithuanian translation (lt_LT).
+  * Adopt the latest thumbnail API in libfm (thumbnail branch) to speed up loading.
+  * Workardound incorrect thumbnail painting caused by bug of QStyledItemDelegate. :-(
+  * Fix a crash caused by accessing data for invalid model index.
+  * Fix a crash caused by accessing data for invalid model index.
+  * Add basic thumbnail support (need the latest thumbnail branch of libfm).
+  * Add archiver integration for file context menus.
+  * Add archiver integration to file context menus.
+  * Add mnemonics for menu items. Make confirm dialog before delete and trash can optional.
+  * Update side pane according to current dir. Little fix.
+  * Implement "Open in Terminal" and "Open as Root".
+  * Implement "Auto Run" for newly inserted removable devices.
+  * Add "Edit Bookmarks" dialog.
+  * Implement "Invert Selection". Little fix of UI, add a Tool menu to main window.
+  * Implement "Create New" menu in the folder popup menu.
+  * Modify make rules for translations. Avoid deleting generated ts files when "make clean". Fix a small error in zh_TW translation.
+  * Add auto-completion to path entry bar.
+  * Rename Fm::Application to Fm::LibFmQt to decrease confusion. Set required Qt version to 4.6.
+  * Load translation files correctly for pcmanfm-qt and libfm-qt.
+  * Add basic skeleton for i18n (using Qt QTranslator & Qt Linguist).
+  * Add separate CMakeLists.txt files for pcmanfm and libfm-qt. Hide more implementation details from libfm-qt headers.
+  * Fix copyright notice in all source files.
+  * Install a pkgconfig file for libfm-qt for use in other projects.
+  * Fix a memory error caused by incorrect array size. Fix incorrect spacing of icons.
+  * Finish chown and chmod supports.
+  * Try to add file opermission settings UI.
+  * Implement very basic drag and drop support.
+  * Supress the incorrect default dnd handling of QListView.
+  * Try to implement Dnd.
+  * Finish desktop preferences.
+  * Improve desktop preferences and apply settings (partially done).
+  * Add desktop preferences dialog.
+  * Apply side pane icon size correctly. Add basic skeleton for archiver integration.
+  * Set shortcuts for frequently used menu options. Implement "rename file" support. Hide tabs when there is only one tab left (optional).
+  * Delete windows properly when they're closed with setAttribute(Qt::WA_DeleteOnClose); Apply settings to windows after clicking OK in the preference dialog.
+  * Improve preferences dialog. Change base class of SidePane to QWidget.
+  * Sync the state of folder popup menu and main menu bar.
+  * Implement sort options for main window.
+  * Fix file sorting options for Fm::FolderMenu.
+  * Correctly implement browse history and fix crashes.
+  * Add very simple browse history (back/forward) handling.
+  * Apply gcc visiblility attributes to export less symbols.
+  * Correctly handle file rename/overwrite during file operations.
+  * Exclude unnecessary files from CPack.
+  * Improve folder popup menu.
+  * Add folder popup menu. Some UI polishing.
+  * Fix a little crash.
+  * Fix crashes when turning off desktop manager.
+  * Show popup menu for blank area of folders.
+  * Do some refactor to make Fm::FolderView cleaner. Add PCManFM::View to do file manager-specific operations.
+  * Move files for libfm-qt and pcmanfm-qt to separate subdirs.
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..553f1e6
--- /dev/null
@@ -0,0 +1,91 @@
+cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
+# CMP0000: Call the cmake_minimum_required() command at the beginning of the top-level
+# CMakeLists.txt file even before calling the project() command.
+# The cmake_minimum_required(VERSION) command implicitly invokes the cmake_policy(VERSION)
+# command to specify that the current project code is written for the given range of CMake
+# versions.
+project(libfm-qt)
+
+set(LIBFM_QT_LIBRARY_NAME "fm-qt" CACHE STRING "fm-qt")
+
+set(LIBFM_QT_API_VERSION_MAJOR 0)
+set(LIBFM_QT_API_VERSION_MINOR 14)
+set(LIBFM_QT_API_VERSION_PATCH 0)
+set(LIBFM_QT_API_VERSION ${LIBFM_QT_API_VERSION_MAJOR}.${LIBFM_QT_API_VERSION_MINOR}.${LIBFM_QT_API_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: 6, revision: 0, age: 0 => version: 6.0.0
+set(LIBFM_QT_ABI_VERSION "6.0.0")
+set(LIBFM_QT_SOVERSION "6")
+
+set(GLIB_MINIMUM_VERSION "2.50.0")
+set(LIBMENUCACHE_MINIMUM_VERSION "1.1.0")
+set(LXQTBT_MINIMUM_VERSION "0.6.0")
+set(QT_MINIMUM_VERSION "5.7.1")
+
+find_package(Qt5Widgets "${QT_MINIMUM_VERSION}" REQUIRED)
+find_package(Qt5LinguistTools "${QT_MINIMUM_VERSION}" REQUIRED)
+find_package(Qt5X11Extras "${QT_MINIMUM_VERSION}" REQUIRED)
+
+find_package(lxqt-build-tools "${LXQTBT_MINIMUM_VERSION}" REQUIRED)
+find_package(GLIB "${GLIB_MINIMUM_VERSION}" REQUIRED COMPONENTS gio gio-unix gobject gthread)
+find_package(MenuCache "${LIBMENUCACHE_MINIMUM_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_API_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
new file mode 100644 (file)
index 0000000..9b102f0
--- /dev/null
@@ -0,0 +1,1890 @@
+# 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 <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> 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 <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> 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
+# <a href = "http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS = 
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href = "http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+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
new file mode 100644 (file)
index 0000000..20fb9c7
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,458 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..1be82fa
--- /dev/null
+++ b/README.md
@@ -0,0 +1,52 @@
+# 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.
+
+
+### Translation (Weblate)
+
+<a href="https://weblate.lxqt.org/projects/lxqt/libfm-qt/">
+<img src="https://weblate.lxqt.org/widgets/lxqt/-/libfm-qt/multi-auto.svg" alt="Translation status" />
+</a>
diff --git a/cmake/fm-qt-config.cmake.in b/cmake/fm-qt-config.cmake.in
new file mode 100644 (file)
index 0000000..a44783f
--- /dev/null
@@ -0,0 +1,41 @@
+#=============================================================================
+# Copyright 2015 Luís Pereira <luis.artur.pereira@gmail.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+#    derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#=============================================================================
+
+@PACKAGE_INIT@
+
+if (CMAKE_VERSION VERSION_LESS 3.0.2)
+    message(FATAL_ERROR \"fm-qt requires at least CMake version 3.0.2\")
+endif()
+
+include(CMakeFindDependencyMacro)
+
+if (NOT TARGET @LIBFM_QT_LIBRARY_NAME@)
+    if (POLICY CMP0024)
+        cmake_policy(SET CMP0024 NEW)
+    endif()
+    include("${CMAKE_CURRENT_LIST_DIR}/@LIBFM_QT_LIBRARY_NAME@-targets.cmake")
+endif()
diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9e9cd2c
--- /dev/null
@@ -0,0 +1,10 @@
+install(FILES
+    "archivers.list"
+    "terminals.list"
+    DESTINATION "${CMAKE_INSTALL_DATADIR}/libfm-qt"
+)
+
+install(FILES
+    "libfm-qt-mimetypes.xml"
+    DESTINATION "${CMAKE_INSTALL_DATADIR}/mime/packages"
+)
diff --git a/data/archivers.list b/data/archivers.list
new file mode 100644 (file)
index 0000000..e1afe7e
--- /dev/null
@@ -0,0 +1,42 @@
+[lxqt-archiver]
+create=lxqt-archiver --add %U
+extract=lxqt-archiver --extract %U
+extract_to=lxqt-archiver --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
+
+[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
new file mode 100644 (file)
index 0000000..7f509a9
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Additional mime-types provided by libfm, adding some
+    missing but frequently seen globs for some common mime-types.
+-->
+<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
+
+  <mime-type type="text/plain">
+    <glob pattern="*.ini"/>
+    <glob pattern="*.inf"/>
+  </mime-type>
+
+  <mime-type type="application/x-ms-dos-executable">
+    <glob pattern="*.com" />
+  </mime-type>
+
+  <mime-type type="application/x-ms-win-installer">
+    <comment>Windows installer</comment>
+    <comment xml:lang="zh_TW">Windows 安裝程式</comment>
+    <glob pattern="*.msi" />
+  </mime-type>
+
+  <mime-type type="application/x-vbscript">
+    <comment>MS VBScript</comment>
+    <glob pattern="*.vbs" />
+  </mime-type>
+
+  <mime-type type="text/x-csharp">
+    <comment xml:lang="en">C# source</comment>
+    <comment xml:lang="zh_TW">C# 程式碼</comment>
+    <glob pattern="*.cs"/>
+  </mime-type>
+
+  <mime-type type="application/x-desktop">
+    <comment xml:lang="zh_TW">應用程式捷徑</comment>
+  </mime-type>
+
+  <mime-type type="application/x-sharedlib">
+    <!--
+        This pattern matching is not very accurate ,but the probability that
+        a file is named like this and it's not a shared lib, is very low.
+     -->
+    <glob pattern="*.dll"/> <!-- Windows dll are shared libs, too -->
+    <glob pattern="*.so.[0-9]" />
+    <glob pattern="*.so.[0-9].*" />
+  </mime-type>
+
+  <mime-type type="application/x-desktop">
+    <glob pattern="*.directory"/>
+  </mime-type>
+
+</mime-info>
diff --git a/data/terminals.list b/data/terminals.list
new file mode 100644 (file)
index 0000000..3c49a10
--- /dev/null
@@ -0,0 +1,80 @@
+[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
new file mode 100644 (file)
index 0000000..ec9d9c2
--- /dev/null
@@ -0,0 +1,281 @@
+set(libfm_core_SRCS
+    # gio gvfs implementations
+    core/vfs/fm-file.c
+    core/vfs/fm-file.h
+    core/vfs/fm-xml-file.c
+    core/vfs/fm-xml-file.h
+    core/vfs/vfs-menu.c
+    core/vfs/vfs-search.c
+    # other legacy C code
+    core/legacy/fm-config.c
+    core/legacy/fm-app-info.c
+    # core data structures
+    core/gobjectptr.h
+    core/filepath.cpp
+    core/iconinfo.cpp
+    core/mimetype.cpp
+    core/fileinfo.cpp
+    core/folder.cpp
+    core/folderconfig.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
+    filedialoghelper.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"
+)
+
+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
+    ${GLIB_LIBRARIES}
+    ${GLIB_GIO_LIBRARIES}
+    ${GLIB_GOBJECT_LIBRARIES}
+    ${GLIB_GTHREAD_LIBRARIES}
+    ${MENUCACHE_LIBRARIES}
+    ${XCB_LIBRARIES}
+    ${EXIF_LIBRARIES}
+)
+
+# set libtool soname
+set_target_properties(${LIBFM_QT_LIBRARY_NAME} PROPERTIES
+    VERSION ${LIBFM_QT_ABI_VERSION}
+    SOVERSION ${LIBFM_QT_SOVERSION}
+)
+
+target_include_directories(${LIBFM_QT_LIBRARY_NAME}
+    PRIVATE "${Qt5Gui_PRIVATE_INCLUDE_DIRS}"
+        core/legacy
+    PUBLIC
+        "${GLIB_INCLUDE_DIRS}"
+        "${GLIB_GIO_UNIX_INCLUDE_DIR}"
+        "${MENUCACHE_INCLUDE_DIRS}"
+        "${XCB_INCLUDE_DIRS}"
+        "${EXIF_INCLUDE_DIRS}"
+    INTERFACE
+        "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
+        "$<BUILD_INTERFACE:${LIBFM_QT_INTREE_INCLUDE_DIR}>"
+)
+
+target_compile_definitions(${LIBFM_QT_LIBRARY_NAME}
+    PRIVATE "LIBFM_QT_DATA_DIR=\"${LIBFM_QT_DATA_DIR}\""
+            "GETTEXT_PACKAGE=\"\""
+    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
new file mode 100644 (file)
index 0000000..ef1ebee
--- /dev/null
@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AppChooserDialog</class>
+ <widget class="QDialog" name="AppChooserDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>432</width>
+    <height>387</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Choose an Application</string>
+  </property>
+  <layout class="QFormLayout" name="formLayout">
+   <property name="fieldGrowthPolicy">
+    <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+   </property>
+   <item row="0" column="1">
+    <widget class="QLabel" name="fileTypeHeader"/>
+   </item>
+   <item row="1" column="0" colspan="2">
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>1</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="tab">
+      <attribute name="title">
+       <string>Installed Applications</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout">
+       <item>
+        <widget class="Fm::AppMenuView" name="appMenuView"/>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tab_2">
+      <attribute name="title">
+       <string>Custom Command</string>
+      </attribute>
+      <layout class="QFormLayout" name="formLayout_2">
+       <item row="0" column="0" colspan="2">
+        <widget class="QLabel" name="label_3">
+         <property name="text">
+          <string>Command line to execute:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0" colspan="2">
+        <widget class="QLineEdit" name="cmdLine"/>
+       </item>
+       <item row="3" column="0">
+        <widget class="QLabel" name="label_4">
+         <property name="text">
+          <string>Application name:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="1">
+        <widget class="QLineEdit" name="appName"/>
+       </item>
+       <item row="2" column="0" colspan="2">
+        <widget class="QLabel" name="label_5">
+         <property name="text">
+          <string>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</string>
+         </property>
+         <property name="textFormat">
+          <enum>Qt::RichText</enum>
+         </property>
+        </widget>
+       </item>
+       <item row="5" column="0" colspan="2">
+        <widget class="QCheckBox" name="keepTermOpen">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="text">
+          <string>Keep terminal window open after command execution</string>
+         </property>
+        </widget>
+       </item>
+       <item row="4" column="0" colspan="2">
+        <widget class="QCheckBox" name="useTerminal">
+         <property name="text">
+          <string>Execute in terminal emulator</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item row="2" column="0" colspan="2">
+    <widget class="QCheckBox" name="setDefault">
+     <property name="text">
+      <string>Set selected application as default action of this file type</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>Fm::AppMenuView</class>
+   <extends>QTreeView</extends>
+   <header>appmenuview.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>AppChooserDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>227</x>
+     <y>359</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>AppChooserDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>295</x>
+     <y>365</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>useTerminal</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>keepTermOpen</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>72</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>79</x>
+     <y>282</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/appchoosercombobox.cpp b/src/appchoosercombobox.cpp
new file mode 100644 (file)
index 0000000..24efa31
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "appchoosercombobox.h"
+#include "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<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &AppChooserComboBox::onCurrentIndexChanged);
+}
+
+AppChooserComboBox::~AppChooserComboBox() {
+}
+
+void AppChooserComboBox::setMimeType(std::shared_ptr<const Fm::MimeType> mimeType) {
+    clear();
+    defaultApp_.reset();
+    appInfos_.clear();
+
+    mimeType_ = std::move(mimeType);
+    if(mimeType_) {
+        const char* typeName = mimeType_->name();
+        defaultApp_ = Fm::GAppInfoPtr{g_app_info_get_default_for_type(typeName, FALSE), false};
+        GList* appInfos_glist = g_app_info_get_all_for_type(typeName);
+        int i = 0;
+        for(GList* l = appInfos_glist; l; l = l->next, ++i) {
+            Fm::GAppInfoPtr app{G_APP_INFO(l->data), false};
+            GIcon* gicon = g_app_info_get_icon(app.get());
+            addItem(gicon ? Fm::IconInfo::fromGIcon(gicon)->qicon(): QIcon(), g_app_info_get_name(app.get()));
+            if(g_app_info_equal(app.get(), defaultApp_.get())) {
+                defaultAppIndex_ = i;
+            }
+            appInfos_.push_back(std::move(app));
+        }
+        g_list_free(appInfos_glist);
+    }
+    // add "Other applications" item
+    insertSeparator(count());
+    addItem(tr("Customize"));
+    if(defaultAppIndex_ != -1) {
+        setCurrentIndex(defaultAppIndex_);
+    }
+}
+
+// returns the currently selected app.
+Fm::GAppInfoPtr AppChooserComboBox::selectedApp() const {
+    // 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
new file mode 100644 (file)
index 0000000..4250298
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_APPCHOOSERCOMBOBOX_H
+#define FM_APPCHOOSERCOMBOBOX_H
+
+#include "libfmqtglobals.h"
+#include <QComboBox>
+
+#include <vector>
+
+#include "core/mimetype.h"
+#include "core/gioptrs.h"
+
+namespace Fm {
+
+class LIBFM_QT_API AppChooserComboBox : public QComboBox {
+    Q_OBJECT
+public:
+    ~AppChooserComboBox();
+    explicit AppChooserComboBox(QWidget* parent);
+
+    void setMimeType(std::shared_ptr<const Fm::MimeType> mimeType);
+
+    const std::shared_ptr<const Fm::MimeType>& mimeType() const {
+        return mimeType_;
+    }
+
+    Fm::GAppInfoPtr selectedApp() const;
+    // const GList* customApps();
+
+    bool isChanged() const;
+
+private Q_SLOTS:
+    void onCurrentIndexChanged(int index);
+
+private:
+    std::shared_ptr<const Fm::MimeType> mimeType_;
+    std::vector<Fm::GAppInfoPtr> appInfos_; // applications used to open the file type
+    Fm::GAppInfoPtr defaultApp_; // default application used to open the file type
+    int defaultAppIndex_;
+    int prevIndex_;
+    bool blockOnCurrentIndexChanged_;
+};
+
+}
+
+#endif // FM_APPCHOOSERCOMBOBOX_H
diff --git a/src/appchooserdialog.cpp b/src/appchooserdialog.cpp
new file mode 100644 (file)
index 0000000..e07243e
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2010-2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ * Copyright 2012-2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "appchooserdialog.h"
+#include "ui_app-chooser-dialog.h"
+#include <QPushButton>
+#include <gio/gdesktopappinfo.h>
+#include <glib/gstdio.h>
+
+namespace Fm {
+
+AppChooserDialog::AppChooserDialog(std::shared_ptr<const Fm::MimeType> mimeType, QWidget* parent, Qt::WindowFlags f):
+    QDialog(parent, f),
+    ui(new Ui::AppChooserDialog()),
+    mimeType_{std::move(mimeType)},
+    canSetDefault_(true) {
+    ui->setupUi(this);
+
+    connect(ui->appMenuView, &AppMenuView::selectionChanged, this, &AppChooserDialog::onSelectionChanged);
+    connect(ui->tabWidget, &QTabWidget::currentChanged, this, &AppChooserDialog::onTabChanged);
+
+    if(!ui->appMenuView->isAppSelected()) {
+        ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);    // disable OK button
+    }
+}
+
+AppChooserDialog::~AppChooserDialog() {
+    delete ui;
+}
+
+bool AppChooserDialog::isSetDefault() const {
+    return ui->setDefault->isChecked();
+}
+
+static void on_temp_appinfo_destroy(gpointer data, GObject* /*objptr*/) {
+    char* filename = (char*)data;
+    if(g_unlink(filename) < 0) {
+        g_critical("failed to remove %s", filename);
+    }
+    /* else
+        qDebug("temp file %s removed", filename); */
+    g_free(filename);
+}
+
+static GAppInfo* app_info_create_from_commandline(const char* commandline,
+        const char* application_name,
+        const char* bin_name,
+        const char* mime_type,
+        gboolean terminal, gboolean keep) {
+    GAppInfo* app = nullptr;
+    char* dirname = g_build_filename(g_get_user_data_dir(), "applications", nullptr);
+    const char* app_basename = strrchr(bin_name, '/');
+
+    if(app_basename) {
+        app_basename++;
+    }
+    else {
+        app_basename = bin_name;
+    }
+    if(g_mkdir_with_parents(dirname, 0700) == 0) {
+        char* filename = g_strdup_printf("%s/userapp-%s-XXXXXX.desktop", dirname, app_basename);
+        int fd = g_mkstemp(filename);
+        if(fd != -1) {
+            GString* content = g_string_sized_new(256);
+            g_string_printf(content,
+                            "[" G_KEY_FILE_DESKTOP_GROUP "]\n"
+                            G_KEY_FILE_DESKTOP_KEY_TYPE "=" G_KEY_FILE_DESKTOP_TYPE_APPLICATION "\n"
+                            G_KEY_FILE_DESKTOP_KEY_NAME "=%s\n"
+                            G_KEY_FILE_DESKTOP_KEY_EXEC "=%s\n"
+                            G_KEY_FILE_DESKTOP_KEY_CATEGORIES "=Other;\n"
+                            G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY "=true\n",
+                            application_name,
+                            commandline
+                           );
+            if(mime_type)
+                g_string_append_printf(content,
+                                       G_KEY_FILE_DESKTOP_KEY_MIME_TYPE "=%s\n",
+                                       mime_type);
+            g_string_append_printf(content,
+                                   G_KEY_FILE_DESKTOP_KEY_TERMINAL "=%s\n",
+                                   terminal ? "true" : "false");
+            if(terminal)
+                g_string_append_printf(content, "X-KeepTerminal=%s\n",
+                                       keep ? "true" : "false");
+            close(fd); /* g_file_set_contents() may fail creating duplicate */
+            if(g_file_set_contents(filename, content->str, content->len, nullptr)) {
+                char* fbname = g_path_get_basename(filename);
+                app = G_APP_INFO(g_desktop_app_info_new(fbname));
+                g_free(fbname);
+                /* if there is mime_type set then created application will be
+                   saved for the mime type (see fm_choose_app_for_mime_type()
+                   below) but if not then we should remove this temp. file */
+                if(!mime_type || !application_name[0])
+                    /* save the name so this file will be removed later */
+                    g_object_weak_ref(G_OBJECT(app), on_temp_appinfo_destroy,
+                                      g_strdup(filename));
+            }
+            else {
+                g_unlink(filename);
+            }
+            g_string_free(content, TRUE);
+        }
+        g_free(filename);
+    }
+    g_free(dirname);
+    return app;
+}
+
+inline static char* get_binary(const char* cmdline, gboolean* arg_found) {
+    /* see if command line contains %f, %F, %u, or %U. */
+    const char* p = strstr(cmdline, " %");
+    if(p) {
+        if(!strchr("fFuU", *(p + 2))) {
+            p = nullptr;
+        }
+    }
+    if(arg_found) {
+        *arg_found = (p != nullptr);
+    }
+    if(p) {
+        return g_strndup(cmdline, p - cmdline);
+    }
+    else {
+        return g_strdup(cmdline);
+    }
+}
+
+GAppInfo* AppChooserDialog::customCommandToApp() {
+    GAppInfo* app = nullptr;
+    QByteArray cmdline = ui->cmdLine->text().toLocal8Bit();
+    QByteArray app_name = ui->appName->text().toUtf8();
+    if(!cmdline.isEmpty()) {
+        gboolean arg_found = FALSE;
+        char* bin1 = get_binary(cmdline.constData(), &arg_found);
+        qDebug("bin1 = %s", bin1);
+        /* see if command line contains %f, %F, %u, or %U. */
+        if(!arg_found) {  /* append %f if no %f, %F, %u, or %U was found. */
+            cmdline += " %f";
+        }
+
+        /* FIXME: is there any better way to do this? */
+        /* We need to ensure that no duplicated items are added */
+        if(mimeType_) {
+            MenuCache* menu_cache;
+            /* see if the command is already in the list of known apps for this mime-type */
+            GList* apps = g_app_info_get_all_for_type(mimeType_->name());
+            GList* l;
+            for(l = apps; l; l = l->next) {
+                GAppInfo* app2 = G_APP_INFO(l->data);
+                const char* cmd = g_app_info_get_commandline(app2);
+                char* bin2 = get_binary(cmd, nullptr);
+                if(g_strcmp0(bin1, bin2) == 0) {
+                    app = G_APP_INFO(g_object_ref(app2));
+                    qDebug("found in app list");
+                    g_free(bin2);
+                    break;
+                }
+                g_free(bin2);
+            }
+            g_list_foreach(apps, (GFunc)g_object_unref, nullptr);
+            g_list_free(apps);
+            if(app) {
+                goto _out;
+            }
+
+            /* see if this command can be found in menu cache */
+            menu_cache = menu_cache_lookup("applications.menu");
+            if(menu_cache) {
+                MenuCacheDir* root_dir = menu_cache_dup_root_dir(menu_cache);
+                if(root_dir) {
+                    GSList* all_apps = menu_cache_list_all_apps(menu_cache);
+                    GSList* l;
+                    for(l = all_apps; l; l = l->next) {
+                        MenuCacheApp* ma = MENU_CACHE_APP(l->data);
+                        const char* exec = menu_cache_app_get_exec(ma);
+                        char* bin2;
+                        if(exec == nullptr) {
+                            g_warning("application %s has no Exec statement", menu_cache_item_get_id(MENU_CACHE_ITEM(ma)));
+                            continue;
+                        }
+                        bin2 = get_binary(exec, nullptr);
+                        if(g_strcmp0(bin1, bin2) == 0) {
+                            app = G_APP_INFO(g_desktop_app_info_new(menu_cache_item_get_id(MENU_CACHE_ITEM(ma))));
+                            qDebug("found in menu cache");
+                            menu_cache_item_unref(MENU_CACHE_ITEM(ma));
+                            g_free(bin2);
+                            break;
+                        }
+                        menu_cache_item_unref(MENU_CACHE_ITEM(ma));
+                        g_free(bin2);
+                    }
+                    g_slist_free(all_apps);
+                    menu_cache_item_unref(MENU_CACHE_ITEM(root_dir));
+                }
+                menu_cache_unref(menu_cache);
+            }
+            if(app) {
+                goto _out;
+            }
+        }
+
+        /* FIXME: g_app_info_create_from_commandline force the use of %f or %u, so this is not we need */
+        app = app_info_create_from_commandline(cmdline.constData(), app_name.constData(), bin1,
+                                               mimeType_ ? mimeType_->name() : nullptr,
+                                               ui->useTerminal->isChecked(), ui->keepTermOpen->isChecked());
+_out:
+        g_free(bin1);
+    }
+    return app;
+}
+
+void AppChooserDialog::accept() {
+    QDialog::accept();
+
+    if(ui->tabWidget->currentIndex() == 0) {
+        selectedApp_ = ui->appMenuView->selectedApp();
+    }
+    else { // custom command line
+        selectedApp_ = customCommandToApp();
+    }
+
+    if(selectedApp_) {
+        if(mimeType_ && g_app_info_get_name(selectedApp_.get())) {
+            /* add this app to the mime-type */
+#if GLIB_CHECK_VERSION(2, 27, 6)
+            g_app_info_set_as_last_used_for_type(selectedApp_.get(), mimeType_->name(), nullptr);
+#else
+            g_app_info_add_supports_type(selectedApp_.get(), mimeType_->name(), nullptr);
+#endif
+            /* if need to set default */
+            if(ui->setDefault->isChecked()) {
+                g_app_info_set_as_default_for_type(selectedApp_.get(), mimeType_->name(), nullptr);
+            }
+        }
+    }
+}
+
+void AppChooserDialog::onSelectionChanged() {
+    bool isAppSelected = ui->appMenuView->isAppSelected();
+    ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isAppSelected);
+}
+
+void AppChooserDialog::setMimeType(std::shared_ptr<const Fm::MimeType> mimeType) {
+    mimeType_ = std::move(mimeType);
+    if(mimeType_) {
+        QString text = tr("Select an application to open \"%1\" files")
+                       .arg(QString::fromUtf8(mimeType_->desc()));
+        ui->fileTypeHeader->setText(text);
+    }
+    else {
+        ui->fileTypeHeader->hide();
+        ui->setDefault->hide();
+    }
+}
+
+void AppChooserDialog::setCanSetDefault(bool value) {
+    canSetDefault_ = value;
+    ui->setDefault->setVisible(value);
+}
+
+void AppChooserDialog::onTabChanged(int index) {
+    if(index == 0) { // app menu view
+        onSelectionChanged();
+    }
+    else if(index == 1) { // custom command
+        ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
+    }
+}
+
+} // namespace Fm
diff --git a/src/appchooserdialog.h b/src/appchooserdialog.h
new file mode 100644 (file)
index 0000000..9dccebf
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2010-2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ * Copyright 2012-2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_APPCHOOSERDIALOG_H
+#define FM_APPCHOOSERDIALOG_H
+
+#include <QDialog>
+#include "libfmqtglobals.h"
+
+#include "core/mimetype.h"
+#include "core/gioptrs.h"
+
+namespace Ui {
+class AppChooserDialog;
+}
+
+namespace Fm {
+
+class LIBFM_QT_API AppChooserDialog : public QDialog {
+    Q_OBJECT
+public:
+    explicit AppChooserDialog(std::shared_ptr<const Fm::MimeType> mimeType, QWidget* parent = nullptr, Qt::WindowFlags f = 0);
+    ~AppChooserDialog();
+
+    virtual void accept();
+
+    void setMimeType(std::shared_ptr<const Fm::MimeType> mimeType);
+
+    const std::shared_ptr<const Fm::MimeType>& mimeType() const {
+        return mimeType_;
+    }
+
+    void setCanSetDefault(bool value);
+
+    bool canSetDefault() const {
+        return canSetDefault_;
+    }
+
+    const Fm::GAppInfoPtr& selectedApp() const {
+        return selectedApp_;
+    }
+
+    bool isSetDefault() const;
+
+private:
+    GAppInfo* customCommandToApp();
+
+private Q_SLOTS:
+    void onSelectionChanged();
+    void onTabChanged(int index);
+
+private:
+    Ui::AppChooserDialog* ui;
+    std::shared_ptr<const Fm::MimeType> mimeType_;
+    bool canSetDefault_;
+    Fm::GAppInfoPtr selectedApp_;
+};
+
+}
+
+#endif // FM_APPCHOOSERDIALOG_H
diff --git a/src/applaunchcontext.cpp b/src/applaunchcontext.cpp
new file mode 100644 (file)
index 0000000..e599485
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "applaunchcontext.h"
+#include <QX11Info>
+#include <X11/Xlib.h>
+
+typedef struct _FmAppLaunchContext {
+  GAppLaunchContext parent;
+}FmAppLaunchContext;
+
+G_DEFINE_TYPE(FmAppLaunchContext, fm_app_launch_context, G_TYPE_APP_LAUNCH_CONTEXT)
+
+static char* fm_app_launch_context_get_display(GAppLaunchContext * /*context*/, GAppInfo * /*info*/, GList * /*files*/) {
+  Display* dpy = QX11Info::display();
+  if(dpy) {
+    char* xstr = DisplayString(dpy);
+    return g_strdup(xstr);
+  }
+  return nullptr;
+}
+
+static char* fm_app_launch_context_get_startup_notify_id(GAppLaunchContext * /*context*/, GAppInfo * /*info*/, GList * /*files*/) {
+  return nullptr;
+}
+
+static void fm_app_launch_context_class_init(FmAppLaunchContextClass* klass) {
+  GAppLaunchContextClass* app_launch_class = G_APP_LAUNCH_CONTEXT_CLASS(klass);
+  app_launch_class->get_display = fm_app_launch_context_get_display;
+  app_launch_class->get_startup_notify_id = fm_app_launch_context_get_startup_notify_id;
+}
+
+static void fm_app_launch_context_init(FmAppLaunchContext* /*context*/) {
+}
+
+FmAppLaunchContext* fm_app_launch_context_new_for_widget(QWidget* /*widget*/) {
+  FmAppLaunchContext* context = (FmAppLaunchContext*)g_object_new(FM_TYPE_APP_LAUNCH_CONTEXT, nullptr);
+  return context;
+}
+
+FmAppLaunchContext* fm_app_launch_context_new() {
+  FmAppLaunchContext* context = (FmAppLaunchContext*)g_object_new(FM_TYPE_APP_LAUNCH_CONTEXT, nullptr);
+  return context;
+}
diff --git a/src/applaunchcontext.h b/src/applaunchcontext.h
new file mode 100644 (file)
index 0000000..606c3b6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_APP_LAUNCHCONTEXT_H
+#define FM_APP_LAUNCHCONTEXT_H
+
+#include "libfmqtglobals.h"
+#include <gio/gio.h>
+#include <QWidget>
+
+#define FM_TYPE_APP_LAUNCH_CONTEXT                             (fm_app_launch_context_get_type())
+#define FM_APP_LAUNCH_CONTEXT(obj)                             (G_TYPE_CHECK_INSTANCE_CAST((obj),\
+                       FM_TYPE_APP_LAUNCH_CONTEXT, FmAppLaunchContext))
+#define FM_APP_LAUNCH_CONTEXT_CLASS(klass)             (G_TYPE_CHECK_CLASS_CAST((klass),\
+                       FM_TYPE_APP_LAUNCH_CONTEXT, FmAppLaunchContextClass))
+#define FM_IS_APP_LAUNCH_CONTEXT(obj)                  (G_TYPE_CHECK_INSTANCE_TYPE((obj),\
+                       FM_TYPE_APP_LAUNCH_CONTEXT))
+#define FM_IS_APP_LAUNCH_CONTEXT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE((klass),\
+                       FM_TYPE_APP_LAUNCH_CONTEXT))
+#define FM_APP_LAUNCH_CONTEXT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS((obj),\
+                       FM_TYPE_APP_LAUNCH_CONTEXT, FmAppLaunchContextClass))
+
+typedef struct _FmAppLaunchContext FmAppLaunchContext;
+
+typedef struct _FmAppLaunchContextClass {
+  GAppLaunchContextClass parent;
+}FmAppLaunchContextClass;
+
+FmAppLaunchContext* fm_app_launch_context_new();
+FmAppLaunchContext* fm_app_launch_context_new_for_widget(QWidget* widget);
+GType fm_app_launch_context_get_type();
+
+#endif // FM_APPLAUNCHCONTEXT_H
diff --git a/src/appmenuview.cpp b/src/appmenuview.cpp
new file mode 100644 (file)
index 0000000..f3ace63
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "appmenuview.h"
+#include <QStandardItemModel>
+#include "appmenuview_p.h"
+#include "core/filepath.h"
+
+#include <gio/gdesktopappinfo.h>
+
+namespace Fm {
+
+AppMenuView::AppMenuView(QWidget* parent):
+    QTreeView(parent),
+    model_(new QStandardItemModel()),
+    menu_cache(nullptr),
+    menu_cache_reload_notify(nullptr) {
+
+    setHeaderHidden(true);
+    setSelectionMode(SingleSelection);
+
+    // initialize model
+    // TODO: share one model among all app menu view widgets
+    // ensure that we're using lxmenu-data (FIXME: should we do this?)
+    QByteArray oldenv = qgetenv("XDG_MENU_PREFIX");
+    qputenv("XDG_MENU_PREFIX", "lxde-");
+    menu_cache = menu_cache_lookup("applications.menu");
+    // if(!oldenv.isEmpty())
+    qputenv("XDG_MENU_PREFIX", oldenv); // restore the original value if needed
+
+    if(menu_cache) {
+        MenuCacheDir* dir = menu_cache_dup_root_dir(menu_cache);
+        menu_cache_reload_notify = menu_cache_add_reload_notify(menu_cache, _onMenuCacheReload, this);
+        if(dir) { /* content of menu is already loaded */
+            addMenuItems(nullptr, dir);
+            menu_cache_item_unref(MENU_CACHE_ITEM(dir));
+        }
+    }
+    setModel(model_);
+    connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &AppMenuView::selectionChanged);
+    selectionModel()->select(model_->index(0, 0), QItemSelectionModel::SelectCurrent);
+}
+
+AppMenuView::~AppMenuView() {
+    delete model_;
+    if(menu_cache) {
+        if(menu_cache_reload_notify) {
+            menu_cache_remove_reload_notify(menu_cache, menu_cache_reload_notify);
+        }
+        menu_cache_unref(menu_cache);
+    }
+}
+
+void AppMenuView::addMenuItems(QStandardItem* parentItem, MenuCacheDir* dir) {
+    GSList* l;
+    GSList* list;
+    /* Iterate over all menu items in this directory. */
+    for(l = list = menu_cache_dir_list_children(dir); l != nullptr; l = l->next) {
+        /* Get the menu item. */
+        MenuCacheItem* menuItem = MENU_CACHE_ITEM(l->data);
+        switch(menu_cache_item_get_type(menuItem)) {
+        case MENU_CACHE_TYPE_NONE:
+        case MENU_CACHE_TYPE_SEP:
+            break;
+        case MENU_CACHE_TYPE_APP:
+        case MENU_CACHE_TYPE_DIR: {
+            AppMenuViewItem* newItem = new AppMenuViewItem(menuItem);
+            if(parentItem) {
+                parentItem->insertRow(parentItem->rowCount(), newItem);
+            }
+            else {
+                model_->insertRow(model_->rowCount(), newItem);
+            }
+
+            if(menu_cache_item_get_type(menuItem) == MENU_CACHE_TYPE_DIR) {
+                addMenuItems(newItem, MENU_CACHE_DIR(menuItem));
+            }
+            break;
+        }
+        }
+    }
+    g_slist_free_full(list, (GDestroyNotify)menu_cache_item_unref);
+}
+
+void AppMenuView::onMenuCacheReload(MenuCache* mc) {
+    MenuCacheDir* dir = menu_cache_dup_root_dir(mc);
+    model_->clear();
+    /* FIXME: preserve original selection */
+    if(dir) {
+        addMenuItems(nullptr, dir);
+        menu_cache_item_unref(MENU_CACHE_ITEM(dir));
+        selectionModel()->select(model_->index(0, 0), QItemSelectionModel::SelectCurrent);
+    }
+}
+
+bool AppMenuView::isAppSelected() const {
+    AppMenuViewItem* item = selectedItem();
+    return (item && item->isApp());
+}
+
+AppMenuViewItem* AppMenuView::selectedItem() const {
+    QModelIndexList selected = selectedIndexes();
+    if(!selected.isEmpty()) {
+        AppMenuViewItem* item = static_cast<AppMenuViewItem*>(model_->itemFromIndex(selected.first()
+                                                                                   ));
+        return item;
+    }
+    return nullptr;
+}
+
+Fm::GAppInfoPtr AppMenuView::selectedApp() const {
+    const char* id = selectedAppDesktopId();
+    return Fm::GAppInfoPtr{id ? G_APP_INFO(g_desktop_app_info_new(id)) : nullptr, false};
+}
+
+QByteArray AppMenuView::selectedAppDesktopFilePath() const {
+    AppMenuViewItem* item = selectedItem();
+    if(item && item->isApp()) {
+        char* path = menu_cache_item_get_file_path(item->item());
+        QByteArray ret(path);
+        g_free(path);
+        return ret;
+    }
+    return QByteArray();
+}
+
+const char* AppMenuView::selectedAppDesktopId() const {
+    AppMenuViewItem* item = selectedItem();
+    if(item && item->isApp()) {
+        return menu_cache_item_get_id(item->item());
+    }
+    return nullptr;
+}
+
+FilePath AppMenuView::selectedAppDesktopPath() const {
+    AppMenuViewItem* item = selectedItem();
+    FilePath path;
+    if(item && item->isApp()) {
+        char* mpath = menu_cache_dir_make_path(MENU_CACHE_DIR(item));
+        path = FilePath::fromUri("menu://applications/").relativePath(mpath + 13 /* skip "/Applications" */);
+        g_free(mpath);
+    }
+    return path;
+}
+
+} // namespace Fm
diff --git a/src/appmenuview.h b/src/appmenuview.h
new file mode 100644 (file)
index 0000000..8ab78e2
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_APPMENUVIEW_H
+#define FM_APPMENUVIEW_H
+
+#include <QTreeView>
+#include "libfmqtglobals.h"
+#include <menu-cache/menu-cache.h>
+
+#include "core/gioptrs.h"
+#include "core/filepath.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;
+
+    FilePath selectedAppDesktopPath() const;
+
+    bool isAppSelected() const;
+
+Q_SIGNALS:
+    void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
+
+private:
+    void addMenuItems(QStandardItem* parentItem, MenuCacheDir* dir);
+    void onMenuCacheReload(MenuCache* mc);
+    static void _onMenuCacheReload(MenuCache* mc, gpointer user_data) {
+        static_cast<AppMenuView*>(user_data)->onMenuCacheReload(mc);
+    }
+
+    AppMenuViewItem* selectedItem() const;
+
+private:
+    // gboolean fm_app_menu_view_is_item_app(, GtkTreeIter* it);
+    QStandardItemModel* model_;
+    MenuCache* menu_cache;
+    MenuCacheNotifyId menu_cache_reload_notify;
+};
+
+}
+
+#endif // FM_APPMENUVIEW_H
diff --git a/src/appmenuview_p.h b/src/appmenuview_p.h
new file mode 100644 (file)
index 0000000..7dd3d53
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_APPMENUVIEW_P_H
+#define FM_APPMENUVIEW_P_H
+
+#include <QStandardItem>
+#include <menu-cache/menu-cache.h>
+#include "core/iconinfo.h"
+
+namespace Fm {
+
+class AppMenuViewItem : public QStandardItem {
+public:
+    explicit AppMenuViewItem(MenuCacheItem* item):
+        item_(menu_cache_item_ref(item)) {
+        std::shared_ptr<const Fm::IconInfo> icon;
+        if(menu_cache_item_get_icon(item)) {
+            icon = Fm::IconInfo::fromName(menu_cache_item_get_icon(item));
+        }
+        setText(menu_cache_item_get_name(item));
+        setEditable(false);
+        setDragEnabled(false);
+        if(icon) {
+            setIcon(icon->qicon());
+        }
+    }
+
+    ~AppMenuViewItem() {
+        menu_cache_item_unref(item_);
+    }
+
+    MenuCacheItem* item() {
+        return item_;
+    }
+
+    int type() const {
+        return menu_cache_item_get_type(item_);
+    }
+
+    bool isApp() {
+        return type() == MENU_CACHE_TYPE_APP;
+    }
+
+    bool isDir() {
+        return type() == MENU_CACHE_TYPE_DIR;
+    }
+
+private:
+    MenuCacheItem* item_;
+};
+
+}
+
+#endif // FM_APPMENUVIEW_P_H
diff --git a/src/bookmarkaction.cpp b/src/bookmarkaction.cpp
new file mode 100644 (file)
index 0000000..502126a
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "bookmarkaction.h"
+
+namespace Fm {
+
+BookmarkAction::BookmarkAction(std::shared_ptr<const Fm::BookmarkItem> item, QObject* parent):
+    QAction(parent),
+    item_(std::move(item)) {
+
+    setText(item_->name());
+}
+
+} // namespace Fm
diff --git a/src/bookmarkaction.h b/src/bookmarkaction.h
new file mode 100644 (file)
index 0000000..08fc73f
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef BOOKMARKACTION_H
+#define BOOKMARKACTION_H
+
+#include "libfmqtglobals.h"
+#include <QAction>
+#include "core/bookmarks.h"
+
+namespace Fm {
+
+// action used to create bookmark menu items
+class LIBFM_QT_API BookmarkAction : public QAction {
+public:
+    explicit BookmarkAction(std::shared_ptr<const Fm::BookmarkItem> item, QObject* parent = 0);
+
+    const std::shared_ptr<const Fm::BookmarkItem>& bookmark() const {
+        return item_;
+    }
+
+    const Fm::FilePath& path() const {
+        return item_->path();
+    }
+
+private:
+    std::shared_ptr<const Fm::BookmarkItem> item_;
+};
+
+}
+
+#endif // BOOKMARKACTION_H
diff --git a/src/browsehistory.cpp b/src/browsehistory.cpp
new file mode 100644 (file)
index 0000000..54671fb
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "browsehistory.h"
+
+namespace Fm {
+
+BrowseHistory::BrowseHistory():
+    currentIndex_(0),
+    maxCount_(10) {
+}
+
+BrowseHistory::~BrowseHistory() {
+}
+
+void BrowseHistory::add(Fm::FilePath path, int scrollPos) {
+    int lastIndex = items_.size() - 1;
+    if(currentIndex_ < lastIndex) {
+        // if we're not at the last item, remove items after the current one.
+        items_.erase(items_.cbegin() + currentIndex_ + 1, items_.cend());
+    }
+
+    if(items_.size() + 1 > static_cast<size_t>(maxCount_)) {
+        // if there are too many items, remove the oldest one.
+        // FIXME: what if currentIndex_ == 0? remove the last item instead?
+        if(currentIndex_ == 0) {
+            items_.erase(items_.cbegin() + lastIndex);
+        }
+        else {
+            items_.erase(items_.cbegin());
+            --currentIndex_;
+        }
+    }
+    // add a path and current scroll position to browse history
+    items_.push_back(BrowseHistoryItem(path, scrollPos));
+    currentIndex_ = items_.size() - 1;
+}
+
+void BrowseHistory::setCurrentIndex(int index) {
+    if(index >= 0 && static_cast<size_t>(index) < items_.size()) {
+        currentIndex_ = index;
+        // FIXME: should we emit a signal for the change?
+    }
+}
+
+bool BrowseHistory::canBackward() const {
+    return (currentIndex_ > 0);
+}
+
+int BrowseHistory::backward() {
+    if(canBackward()) {
+        --currentIndex_;
+    }
+    return currentIndex_;
+}
+
+bool BrowseHistory::canForward() const {
+    return (static_cast<size_t>(currentIndex_) + 1 < items_.size());
+}
+
+int BrowseHistory::forward() {
+    if(canForward()) {
+        ++currentIndex_;
+    }
+    return currentIndex_;
+}
+
+void BrowseHistory::setMaxCount(int maxCount) {
+    maxCount_ = maxCount;
+    if(items_.size() > static_cast<size_t>(maxCount)) {
+        // TODO: remove some items
+    }
+}
+
+
+} // namespace Fm
diff --git a/src/browsehistory.h b/src/browsehistory.h
new file mode 100644 (file)
index 0000000..2a99ab9
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_BROWSEHISTORY_H
+#define FM_BROWSEHISTORY_H
+
+#include "libfmqtglobals.h"
+#include <vector>
+
+#include "core/filepath.h"
+
+namespace Fm {
+
+// class used to story browsing history of folder views
+// We use this class to replace FmNavHistory provided by libfm since
+// the original Libfm API is hard to use and confusing.
+
+class LIBFM_QT_API BrowseHistoryItem {
+public:
+
+    explicit BrowseHistoryItem():
+        scrollPos_(0) {
+    }
+
+    explicit BrowseHistoryItem(Fm::FilePath path, int scrollPos = 0):
+        path_(std::move(path)),
+        scrollPos_(scrollPos) {
+    }
+
+    BrowseHistoryItem(const BrowseHistoryItem& other) = default;
+
+    ~BrowseHistoryItem() {
+    }
+
+    BrowseHistoryItem& operator=(const BrowseHistoryItem& other) {
+        path_ = other.path_;
+        scrollPos_ = other.scrollPos_;
+        return *this;
+    }
+
+    Fm::FilePath path() const {
+        return path_;
+    }
+
+    int scrollPos() const {
+        return scrollPos_;
+    }
+
+    void setScrollPos(int pos) {
+        scrollPos_ = pos;
+    }
+
+private:
+    Fm::FilePath path_;
+    int scrollPos_;
+    // TODO: we may need to store current selection as well.
+};
+
+class LIBFM_QT_API BrowseHistory {
+
+public:
+    BrowseHistory();
+    virtual ~BrowseHistory();
+
+    int currentIndex() const {
+        return currentIndex_;
+    }
+    void setCurrentIndex(int index);
+
+    Fm::FilePath currentPath() const {
+        return items_[currentIndex_].path();
+    }
+
+    int currentScrollPos() const {
+        return items_[currentIndex_].scrollPos();
+    }
+
+    BrowseHistoryItem& currentItem() {
+        return items_[currentIndex_];
+    }
+
+    size_t size() const {
+        return items_.size();
+    }
+
+    BrowseHistoryItem& at(int index) {
+        return items_[index];
+    }
+
+    void add(Fm::FilePath path, int scrollPos = 0);
+
+    bool canForward() const;
+
+    bool canBackward() const;
+
+    int backward();
+
+    int forward();
+
+    int maxCount() const {
+        return maxCount_;
+    }
+
+    void setMaxCount(int maxCount);
+
+private:
+    std::vector<BrowseHistoryItem> items_;
+    int currentIndex_;
+    int maxCount_;
+};
+
+}
+
+#endif // FM_BROWSEHISTORY_H
diff --git a/src/cachedfoldermodel.cpp b/src/cachedfoldermodel.cpp
new file mode 100644 (file)
index 0000000..defa398
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "cachedfoldermodel.h"
+
+namespace Fm {
+
+CachedFolderModel::CachedFolderModel(const std::shared_ptr<Fm::Folder>& folder):
+    FolderModel(),
+    refCount(1) {
+    FolderModel::setFolder(folder);
+}
+
+CachedFolderModel::~CachedFolderModel() {
+    // qDebug("delete CachedFolderModel");
+}
+
+CachedFolderModel* CachedFolderModel::modelFromFolder(const std::shared_ptr<Fm::Folder>& folder) {
+    QVariant cache = folder->property(cacheKey);
+    CachedFolderModel* model = cache.value<CachedFolderModel*>();
+    if(model) {
+        model->ref();
+    }
+    else {
+        model = new CachedFolderModel(folder);
+        cache = QVariant::fromValue(model);
+        folder->setProperty(cacheKey, cache);
+    }
+    return model;
+}
+
+CachedFolderModel* CachedFolderModel::modelFromPath(const Fm::FilePath& path) {
+    auto folder = Fm::Folder::fromPath(path);
+    if(folder) {
+        CachedFolderModel* model = modelFromFolder(folder);
+        return model;
+    }
+    return nullptr;
+}
+
+void CachedFolderModel::unref() {
+    // qDebug("unref cache");
+    --refCount;
+    if(refCount <= 0) {
+        folder()->setProperty(cacheKey, QVariant());
+        delete(this);
+    }
+}
+
+
+} // namespace Fm
diff --git a/src/cachedfoldermodel.h b/src/cachedfoldermodel.h
new file mode 100644 (file)
index 0000000..ccd7a3e
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_CACHEDFOLDERMODEL_H
+#define FM_CACHEDFOLDERMODEL_H
+
+#include "libfmqtglobals.h"
+#include "foldermodel.h"
+
+#include "core/folder.h"
+
+namespace Fm {
+
+// FIXME: deprecate CachedFolderModel later (ugly API design with manual ref()/unref())
+class LIBFM_QT_API CachedFolderModel : public FolderModel {
+    Q_OBJECT
+public:
+    explicit CachedFolderModel(const std::shared_ptr<Fm::Folder>& folder);
+    void ref() {
+        ++refCount;
+    }
+    void unref();
+
+    static CachedFolderModel* modelFromFolder(const std::shared_ptr<Fm::Folder>& folder);
+    static CachedFolderModel* modelFromPath(const Fm::FilePath& path);
+
+private:
+    virtual ~CachedFolderModel();
+
+private:
+    int refCount;
+    constexpr static const char* cacheKey = "CachedFolderModel";
+};
+
+
+}
+
+#endif // FM_CACHEDFOLDERMODEL_H
diff --git a/src/colorbutton.cpp b/src/colorbutton.cpp
new file mode 100644 (file)
index 0000000..ac95b45
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "colorbutton.h"
+#include <QColorDialog>
+
+namespace Fm {
+
+ColorButton::ColorButton(QWidget* parent): QPushButton(parent) {
+    connect(this, &QPushButton::clicked, this, &ColorButton::onClicked);
+}
+
+ColorButton::~ColorButton() {
+
+}
+
+void ColorButton::onClicked() {
+    QColorDialog dlg(color_);
+    if(dlg.exec() == QDialog::Accepted) {
+        setColor(dlg.selectedColor());
+    }
+}
+
+void ColorButton::setColor(const QColor& color) {
+    if(color != color_) {
+        color_ = color;
+        // use qss instead of QPalette to set the background color
+        // otherwise, this won't work when using the gtk style.
+        QString style = QString("QPushButton{background-color:%1;}").arg(color.name());
+        setStyleSheet(style);
+        Q_EMIT changed();
+    }
+}
+
+
+} // namespace Fm
diff --git a/src/colorbutton.h b/src/colorbutton.h
new file mode 100644 (file)
index 0000000..d5fa89d
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_COLORBUTTON_H
+#define FM_COLORBUTTON_H
+
+#include "libfmqtglobals.h"
+#include <QPushButton>
+#include <QColor>
+
+namespace Fm {
+
+class LIBFM_QT_API ColorButton : public QPushButton {
+    Q_OBJECT
+
+public:
+    explicit ColorButton(QWidget* parent = 0);
+    virtual ~ColorButton();
+
+    void setColor(const QColor&);
+
+    QColor color() const {
+        return color_;
+    }
+
+Q_SIGNALS:
+    void changed();
+
+private Q_SLOTS:
+    void onClicked();
+
+private:
+    QColor color_;
+};
+
+}
+
+#endif // FM_COLORBUTTON_H
diff --git a/src/core/archiver.cpp b/src/core/archiver.cpp
new file mode 100644 (file)
index 0000000..b38990e
--- /dev/null
@@ -0,0 +1,174 @@
+#include "libfmqtglobals.h"
+#include "archiver.h"
+
+#include <string.h>
+#include <glib.h>
+#include <gio/gdesktopappinfo.h>
+
+#include <string>
+
+namespace Fm {
+
+Archiver* Archiver::defaultArchiver_ = nullptr;  // static
+std::vector<std::unique_ptr<Archiver>> Archiver::allArchivers_;  // static
+
+Archiver::Archiver() {
+}
+
+bool Archiver::isMimeTypeSupported(const char* type) {
+    char** p;
+    if(G_UNLIKELY(!type)) {
+        return false;
+    }
+    for(p = mimeTypes_.get(); *p; ++p) {
+        if(strcmp(*p, type) == 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool Archiver::launchProgram(GAppLaunchContext* ctx, const char* cmd, const FilePathList& files, const FilePath& dir) {
+    char* _cmd = NULL;
+    const char* dir_place_holder;
+    GKeyFile* dummy;
+
+    if(dir.isValid() && (dir_place_holder = strstr(cmd, "%d"))) {
+        CStrPtr dir_str;
+        int len;
+        if(strstr(cmd, "%U") || strstr(cmd, "%u")) { /* supports URI */
+            dir_str = dir.uri();
+        }
+        else {
+            dir_str = dir.localPath();
+        }
+
+        // FIXME: remove libfm dependency here
+        /* replace all % with %% so encoded URI can be handled correctly when parsing Exec key. */
+        std::string percentEscapedDir;
+        for(auto p = dir_str.get(); *p; ++p) {
+            percentEscapedDir += *p;
+            if(*p == '%') {
+                percentEscapedDir += '%';
+            }
+        }
+
+        /* quote the path or URI */
+        dir_str = CStrPtr{g_shell_quote(percentEscapedDir.c_str())};
+
+        len = strlen(cmd) - 2 + strlen(dir_str.get()) + 1;
+        _cmd = (char*)g_malloc(len);
+        len = (dir_place_holder - cmd);
+        strncpy(_cmd, cmd, len);
+        strcpy(_cmd + len, dir_str.get());
+        strcat(_cmd, dir_place_holder + 2);
+        cmd = _cmd;
+    }
+
+    /* create a fake key file to cheat GDesktopAppInfo */
+    dummy = g_key_file_new();
+    g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Type", "Application");
+    g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Name", program_.get());
+
+    /* replace all % with %% so encoded URI can be handled correctly when parsing Exec key. */
+    g_key_file_set_string(dummy, G_KEY_FILE_DESKTOP_GROUP, "Exec", cmd);
+    GAppInfoPtr app{reinterpret_cast<GAppInfo*>(g_desktop_app_info_new_from_keyfile(dummy)), false};
+
+    g_key_file_free(dummy);
+    g_debug("cmd = %s", cmd);
+    if(app) {
+        GList* uris = NULL;
+        for(auto& file: files) {
+            uris = g_list_prepend(uris, g_strdup(file.uri().get()));
+        }
+        g_app_info_launch_uris(app.get(), uris, ctx, NULL);
+        g_list_foreach(uris, (GFunc)g_free, NULL);
+        g_list_free(uris);
+    }
+    g_free(_cmd);
+    return true;
+}
+
+bool Archiver::createArchive(GAppLaunchContext* ctx, const FilePathList& files) {
+    if(createCmd_ && !files.empty()) {
+        launchProgram(ctx, createCmd_.get(), files, FilePath{});
+    }
+    return false;
+}
+
+bool Archiver::extractArchives(GAppLaunchContext* ctx, const FilePathList& files) {
+    if(extractCmd_ && !files.empty()) {
+        launchProgram(ctx, extractCmd_.get(), files, FilePath{});
+    }
+    return false;
+}
+
+bool Archiver::extractArchivesTo(GAppLaunchContext* ctx, const FilePathList& files, const FilePath& dest_dir) {
+    if(extractToCmd_ && !files.empty()) {
+        launchProgram(ctx, extractToCmd_.get(), files, dest_dir);
+    }
+    return false;
+}
+
+// static
+Archiver* Archiver::defaultArchiver() {
+    allArchivers(); // to have a preliminary default archiver
+    return defaultArchiver_;
+}
+
+void Archiver::setDefaultArchiverByName(const char *name) {
+    if(name) {
+        auto& all = allArchivers();
+        for(auto& archiver: all) {
+            if(archiver->program_ && strcmp(archiver->program_.get(), name) == 0) {
+                defaultArchiver_ = archiver.get();
+                break;
+            }
+        }
+    }
+}
+
+// static
+void Archiver::setDefaultArchiver(Archiver* archiver) {
+    if(archiver) {
+        defaultArchiver_ = archiver;
+    }
+}
+
+// static
+const std::vector<std::unique_ptr<Archiver> >& Archiver::allArchivers() {
+    // load all archivers on demand
+    if(allArchivers_.empty()) {
+        GKeyFile* kf = g_key_file_new();
+        if(g_key_file_load_from_file(kf, LIBFM_QT_DATA_DIR "/archivers.list", G_KEY_FILE_NONE, NULL)) {
+            gsize n_archivers;
+            CStrArrayPtr programs{g_key_file_get_groups(kf, &n_archivers)};
+            if(programs) {
+                gsize i;
+                for(i = 0; i < n_archivers; ++i) {
+                    auto program = programs[i];
+                    std::unique_ptr<Archiver> archiver{new Archiver{}};
+                    archiver->createCmd_ = CStrPtr{g_key_file_get_string(kf, program, "create", NULL)};
+                    archiver->extractCmd_ = CStrPtr{g_key_file_get_string(kf, program, "extract", NULL)};
+                    archiver->extractToCmd_ = CStrPtr{g_key_file_get_string(kf, program, "extract_to", NULL)};
+                    archiver->mimeTypes_ = CStrArrayPtr{g_key_file_get_string_list(kf, program, "mime_types", NULL, NULL)};
+                    archiver->program_ = CStrPtr{g_strdup(program)};
+
+                    // if default archiver is not set, find the first program existing in the current system.
+                    if(!defaultArchiver_) {
+                        CStrPtr fullPath{g_find_program_in_path(program)};
+                        if(fullPath) {
+                            defaultArchiver_ = archiver.get();
+                        }
+                    }
+
+                    allArchivers_.emplace_back(std::move(archiver));
+                }
+            }
+        }
+        g_key_file_free(kf);
+    }
+    return allArchivers_;
+}
+
+} // namespace Fm
diff --git a/src/core/archiver.h b/src/core/archiver.h
new file mode 100644 (file)
index 0000000..ba5368d
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef ARCHIVER_H
+#define ARCHIVER_H
+
+#include "../libfmqtglobals.h"
+#include "filepath.h"
+#include "gioptrs.h"
+
+#include <vector>
+#include <memory>
+
+namespace Fm {
+
+class LIBFM_QT_API Archiver {
+public:
+    Archiver();
+
+    bool isMimeTypeSupported(const char* type);
+
+    bool canCreateArchive() const {
+        return createCmd_ != nullptr;
+    }
+
+    bool createArchive(GAppLaunchContext* ctx, const FilePathList& files);
+
+    bool canExtractArchives() const {
+        return extractCmd_ != nullptr;
+    }
+
+    bool extractArchives(GAppLaunchContext* ctx, const FilePathList& files);
+
+    bool canExtractArchivesTo() const {
+        return extractToCmd_ != nullptr;
+    }
+
+    bool extractArchivesTo(GAppLaunchContext* ctx, const FilePathList& files, const FilePath& dest_dir);
+
+    /* get default GUI archivers used by libfm */
+    static Archiver* defaultArchiver();
+
+    /* set default GUI archivers used by libfm */
+    static void setDefaultArchiverByName(const char* name);
+
+    /* set default GUI archivers used by libfm */
+    static void setDefaultArchiver(Archiver* archiver);
+
+    /* get a list of FmArchiver* of all GUI archivers known to libfm */
+    static const std::vector<std::unique_ptr<Archiver>>& allArchivers();
+
+    const char* program() const {
+        return program_.get();
+    }
+
+private:
+    bool launchProgram(GAppLaunchContext* ctx, const char* cmd, const FilePathList& files, const FilePath &dir);
+
+private:
+    CStrPtr program_;
+    CStrPtr createCmd_;
+    CStrPtr extractCmd_;
+    CStrPtr extractToCmd_;
+    CStrArrayPtr mimeTypes_;
+
+    static Archiver* defaultArchiver_;
+    static std::vector<std::unique_ptr<Archiver>> allArchivers_;
+};
+
+} // namespace Fm
+
+#endif // ARCHIVER_H
diff --git a/src/core/basicfilelauncher.cpp b/src/core/basicfilelauncher.cpp
new file mode 100644 (file)
index 0000000..6f185c0
--- /dev/null
@@ -0,0 +1,392 @@
+#include "basicfilelauncher.h"
+#include "fileinfojob.h"
+#include "mountoperation.h"
+
+#include <gio/gdesktopappinfo.h>
+#include <glib/gi18n.h>
+
+#include <unordered_map>
+#include <string>
+
+#include <QObject>
+#include <QEventLoop>
+#include <QDebug>
+
+#include "legacy/fm-app-info.h"
+
+namespace Fm {
+
+BasicFileLauncher::BasicFileLauncher():
+    quickExec_{false} {
+}
+
+BasicFileLauncher::~BasicFileLauncher() {
+}
+
+bool BasicFileLauncher::launchFiles(const FileInfoList& fileInfos, GAppLaunchContext* ctx) {
+    std::unordered_map<std::string, FileInfoList> mimeTypeToFiles;
+    FileInfoList folderInfos;
+    FilePathList pathsToLaunch;
+    // classify files according to different mimetypes
+    for(auto& fileInfo : fileInfos) {
+        /*
+        qDebug("path: %s, type: %s, target: %s, isDir: %i, isShortcut: %i, isMountable: %i, isDesktopEntry: %i",
+               fileInfo->path().toString().get(), fileInfo->mimeType()->name(), fileInfo->target().c_str(),
+               fileInfo->isDir(), fileInfo->isShortcut(), fileInfo->isMountable(), fileInfo->isDesktopEntry());
+        */
+        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 if(fileInfo->isDir()) {
+            folderInfos.emplace_back(fileInfo);
+        }
+        else {
+            auto& mimeType = fileInfo->mimeType();
+            mimeTypeToFiles[mimeType->name()].emplace_back(fileInfo);
+        }
+    }
+
+    // open folders
+    if(!folderInfos.empty()) {
+        GErrorPtr err;
+        openFolder(ctx, folderInfos, err);
+    }
+
+    // open files of different mime-types with their default app
+    for(auto& typeFiles : mimeTypeToFiles) {
+        auto& mimeType = typeFiles.first;
+        auto& files = typeFiles.second;
+        GErrorPtr err;
+        GAppInfoPtr app{g_app_info_get_default_for_type(mimeType.c_str(), false), false};
+        if(!app) {
+            app = chooseApp(files, mimeType.c_str(), err);
+        }
+        if(app) {
+            launchWithApp(app.get(), files.paths(), ctx);
+        }
+    }
+
+    if(!pathsToLaunch.empty()) {
+        launchPaths(pathsToLaunch, ctx);
+    }
+
+    return true;
+}
+
+bool BasicFileLauncher::launchPaths(FilePathList paths, GAppLaunchContext* ctx) {
+    // FIXME: blocking with an event loop is not a good design :-(
+    QEventLoop eventLoop;
+    auto job = new FileInfoJob{paths};
+    job->setAutoDelete(false);  // do not automatically delete the job since we want its results later.
+
+    GObjectPtr<GAppLaunchContext> ctxPtr{ctx};
+
+    // error handling (for example: handle path not mounted error)
+    QObject::connect(job, &FileInfoJob::error,
+            &eventLoop, [this, job, ctx](const GErrorPtr & err, Job::ErrorSeverity /* severity */ , Job::ErrorAction &act) {
+        auto path = job->currentPath();
+        if(showError(ctx, err, path, nullptr)) {
+            // the user handled the error and ask for retry
+            act = Job::ErrorAction::RETRY;
+        }
+    }, Qt::BlockingQueuedConnection);  // BlockingQueuedConnection is required here to pause the job and wait for user response
+
+    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 */, const GErrorPtr & /* err */, const FilePath& /* path */, const FileInfoPtr& /* info */) {
+    return false;
+}
+
+int BasicFileLauncher::ask(const char* /* msg */, char* const* /* btn_labels */, int default_btn) {
+    return default_btn;
+}
+
+bool BasicFileLauncher::launchWithApp(GAppInfo* app, const FilePathList& paths, GAppLaunchContext* ctx) {
+    GList* uris = nullptr;
+    for(auto& path : paths) {
+        auto uri = path.uri();
+        uris = g_list_prepend(uris, uri.release());
+    }
+    GErrorPtr err;
+    bool ret = bool(g_app_info_launch_uris(app, uris, ctx, &err));
+    g_list_foreach(uris, reinterpret_cast<GFunc>(g_free), nullptr);
+    g_list_free(uris);
+    if(!ret) {
+        // FIXME: show error for all files
+        showError(ctx, err, paths.empty() ? FilePath{} : 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_ || fileInfo->isTrustable()) ? 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) {
+        // don't call launchWithApp() because it calls g_app_info_launch_uris(),
+        // which uses the hard-coded terminal list of GLib -> gdesktopappinfo.c
+        GList* uris = nullptr;
+        for(auto& path : paths) {
+            auto uri = path.uri();
+            uris = g_list_prepend(uris, uri.release());
+        }
+        GErrorPtr err;
+        ret = bool(fm_app_info_launch(app, uris, ctx, &err));
+        g_list_foreach(uris, reinterpret_cast<GFunc>(g_free), nullptr);
+        g_list_free(uris);
+        if(!ret) {
+            // FIXME: show error for all files
+            showError(ctx, err, paths.empty() ? FilePath{} : paths[0]);
+        }
+    }
+    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();
+
+    // if we know the target is a dir, we are not going to open it using other apps
+    // for example: `network:///smb-root' is a shortcut targeting `smb:///' and it's also a dir
+    if(fileInfo->isDir()) {
+        qDebug("shortcut is dir: %s", target.c_str());
+        return FilePath::fromPathStr(target.c_str());
+    }
+
+    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
+                || strcmp(scheme.get(), "menu") == 0) {
+            return FilePath::fromUri(target.c_str());
+        }
+        else {
+            // ask gio to launch the default handler for the uri scheme
+            if(GAppInfoPtr app{g_app_info_get_default_for_uri_scheme(scheme.get()), false}) {
+                FilePathList uris{FilePath::fromUri(target.c_str())};
+                launchWithApp(app.get(), uris, ctx);
+            }
+            else {
+                GErrorPtr err{G_IO_ERROR, G_IO_ERROR_FAILED,
+                              QObject::tr("No default application is set to launch '%1'")
+                              .arg(target.c_str())};
+                showError(nullptr, err);
+            }
+        }
+    }
+    else {
+        // see it as a local path
+        return FilePath::fromLocalPath(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_ || fileInfo->isTrustable()) ? 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
new file mode 100644 (file)
index 0000000..3b1545d
--- /dev/null
@@ -0,0 +1,72 @@
+#ifndef BASICFILELAUNCHER_H
+#define BASICFILELAUNCHER_H
+
+#include "../libfmqtglobals.h"
+
+#include "fileinfo.h"
+#include "filepath.h"
+#include "mimetype.h"
+
+#include <gio/gio.h>
+
+namespace Fm {
+
+class LIBFM_QT_API BasicFileLauncher {
+public:
+
+    enum class ExecAction {
+        NONE,
+        DIRECT_EXEC,
+        EXEC_IN_TERMINAL,
+        OPEN_WITH_DEFAULT_APP,
+        CANCEL
+    };
+
+    explicit BasicFileLauncher();
+    virtual ~BasicFileLauncher();
+
+    bool launchFiles(const FileInfoList &fileInfos, GAppLaunchContext* ctx = nullptr);
+
+    bool launchPaths(FilePathList paths, GAppLaunchContext* ctx = nullptr);
+
+    bool launchDesktopEntry(const FileInfoPtr &fileInfo, const FilePathList& paths = FilePathList{}, GAppLaunchContext* ctx = nullptr);
+
+    bool launchDesktopEntry(const char* desktopEntryName, const FilePathList& paths = FilePathList{}, GAppLaunchContext* ctx = nullptr);
+
+    bool launchWithDefaultApp(const FileInfoPtr& fileInfo, GAppLaunchContext* ctx = nullptr);
+
+    bool launchWithApp(GAppInfo* app, const FilePathList& paths, GAppLaunchContext* ctx = nullptr);
+
+    bool launchExecutable(const FileInfoPtr &fileInfo, GAppLaunchContext* ctx = nullptr);
+
+    bool quickExec() const {
+        return quickExec_;
+    }
+
+    void setQuickExec(bool value) {
+        quickExec_ = value;
+    }
+
+protected:
+
+    virtual GAppInfoPtr chooseApp(const FileInfoList& fileInfos, const char* mimeType, GErrorPtr& err);
+
+    virtual bool openFolder(GAppLaunchContext* ctx, const FileInfoList& folderInfos, GErrorPtr& err);
+
+    virtual bool showError(GAppLaunchContext* ctx, const 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
new file mode 100644 (file)
index 0000000..93996c8
--- /dev/null
@@ -0,0 +1,217 @@
+#include "bookmarks.h"
+#include "cstrptr.h"
+#include <algorithm>
+#include <QTimer>
+#include <QStandardPaths>
+
+namespace Fm {
+
+std::weak_ptr<Bookmarks> Bookmarks::globalInstance_;
+
+static inline CStrPtr get_legacy_bookmarks_file(void) {
+    return CStrPtr{g_build_filename(g_get_home_dir(), ".gtk-bookmarks", nullptr)};
+}
+
+static inline CStrPtr get_new_bookmarks_file(void) {
+    return CStrPtr{g_build_filename(g_get_user_config_dir(), "gtk-3.0", "bookmarks", nullptr)};
+}
+
+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<GFileMonitor>{g_file_monitor_file(file.gfile().get(), G_FILE_MONITOR_NONE, nullptr, nullptr), false};
+    if(mon) {
+        g_signal_connect(mon.get(), "changed", G_CALLBACK(_onFileChanged), this);
+    }
+}
+
+Bookmarks::~Bookmarks() {
+    if(mon) {
+        g_signal_handlers_disconnect_by_data(mon.get(), this);
+    }
+}
+
+const std::shared_ptr<const BookmarkItem>& Bookmarks::insert(const FilePath& path, const QString& name, int pos) {
+    const auto insert_pos = (pos < 0 || static_cast<size_t>(pos) > items_.size()) ? items_.cend() : items_.cbegin() + pos;
+    auto it = items_.insert(insert_pos, std::make_shared<const BookmarkItem>(path, name));
+    queueSave();
+    return *it;
+}
+
+void Bookmarks::remove(const std::shared_ptr<const BookmarkItem>& item) {
+    items_.erase(std::remove(items_.begin(), items_.end(), item), items_.end());
+    queueSave();
+}
+
+void Bookmarks::reorder(const std::shared_ptr<const BookmarkItem>& item, int pos) {
+    auto old_it = std::find(items_.cbegin(), items_.cend(), item);
+    if(old_it == items_.cend())
+        return;
+    std::shared_ptr<const BookmarkItem> newItem = item;
+    auto old_pos = old_it - items_.cbegin();
+    items_.erase(old_it);
+    if(old_pos < pos)
+        --pos;
+    auto new_it = items_.cbegin() + pos;
+    if(new_it > items_.cend())
+        new_it = items_.cend();
+    items_.insert(new_it, std::move(newItem));
+    queueSave();
+}
+
+void Bookmarks::rename(const std::shared_ptr<const BookmarkItem>& item, QString new_name) {
+    auto it = std::find_if(items_.cbegin(), items_.cend(), [item](const std::shared_ptr<const BookmarkItem>& elem) {
+        return elem->path() == item->path();
+    });
+    if(it != items_.cend()) {
+        // create a new item to replace the old one
+        // we do not modify the old item directly since this data structure is shared with others
+        it = items_.insert(it, std::make_shared<const BookmarkItem>(item->path(), new_name));
+        items_.erase(it + 1); // remove the old item
+        queueSave();
+    }
+}
+
+std::shared_ptr<Bookmarks> Bookmarks::globalInstance() {
+    auto bookmarks = globalInstance_.lock();
+    if(!bookmarks) {
+        bookmarks = std::make_shared<Bookmarks>();
+        globalInstance_ = bookmarks;
+    }
+    return bookmarks;
+}
+
+void Bookmarks::save() {
+    std::string buf;
+    // G_LOCK(bookmarks);
+    for(auto& item: items_) {
+        auto uri = item->path().uri();
+        buf += uri.get();
+        buf += ' ';
+        buf += item->name().toUtf8().constData();
+        buf += '\n';
+    }
+    idle_handler = false;
+    // G_UNLOCK(bookmarks);
+    GError* err = nullptr;
+    if(!g_file_replace_contents(file.gfile().get(), buf.c_str(), buf.length(), nullptr,
+                                FALSE, G_FILE_CREATE_NONE, nullptr, nullptr, &err)) {
+        g_critical("%s", err->message);
+        g_error_free(err);
+    }
+    /* we changed bookmarks list, let inform who interested in that */
+    Q_EMIT changed();
+}
+
+void Bookmarks::load() {
+    auto fpath = file.localPath();
+    FILE* f;
+    char buf[1024];
+    /* load the file */
+    f = fopen(fpath.get(), "r");
+    if(f) {
+        while(fgets(buf, 1024, f)) {
+            // format of each line in the bookmark file:
+            // <URI> <name>\n
+            char* sep;
+            sep = strchr(buf, '\n');
+            if(sep) {
+                *sep = '\0';
+            }
+
+            QString name;
+            sep = strchr(buf, ' ');  // find the separator between URI and name
+            if(sep) {
+                *sep = '\0';
+                name = sep + 1;
+            }
+            auto uri = buf;
+            if(uri[0] != '\0') {
+                items_.push_back(std::make_shared<BookmarkItem>(FilePath::fromUri(uri), name));
+            }
+        }
+        fclose(f);
+    }
+}
+
+void Bookmarks::onFileChanged(GFileMonitor* /*mon*/, GFile* /*gf*/, GFile* /*other*/, GFileMonitorEvent /*evt*/) {
+    // reload the bookmarks
+    items_.clear();
+    load();
+    Q_EMIT changed();
+}
+
+
+void Bookmarks::queueSave() {
+    if(!idle_handler) {
+        QTimer::singleShot(0, this, &Bookmarks::save);
+        idle_handler = true;
+    }
+}
+
+
+} // namespace Fm
diff --git a/src/core/bookmarks.h b/src/core/bookmarks.h
new file mode 100644 (file)
index 0000000..7b5af79
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef FM2_BOOKMARKS_H
+#define FM2_BOOKMARKS_H
+
+#include <QObject>
+#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<const IconInfo>& icon() const {
+        return icon_;
+    }
+
+private:
+    void setName(const QString& name) {
+        name_ = name;
+    }
+
+private:
+    FilePath path_;
+    QString name_;
+    std::shared_ptr<const IconInfo> icon_;
+};
+
+
+class LIBFM_QT_API Bookmarks : public QObject {
+    Q_OBJECT
+public:
+    explicit Bookmarks(QObject* parent = 0);
+
+    ~Bookmarks();
+
+    const std::shared_ptr<const BookmarkItem> &insert(const FilePath& path, const QString& name, int pos);
+
+    void remove(const std::shared_ptr<const BookmarkItem>& item);
+
+    void reorder(const std::shared_ptr<const BookmarkItem> &item, int pos);
+
+    void rename(const std::shared_ptr<const BookmarkItem>& item, QString new_name);
+
+    const std::vector<std::shared_ptr<const BookmarkItem>>& items() const {
+        return items_;
+    }
+
+    static std::shared_ptr<Bookmarks> globalInstance();
+
+Q_SIGNALS:
+    void changed();
+
+private Q_SLOTS:
+    void save();
+
+private:
+    void load();
+    void queueSave();
+
+    static void _onFileChanged(GFileMonitor* mon, GFile* gf, GFile* other, GFileMonitorEvent evt, Bookmarks* _this) {
+        _this->onFileChanged(mon, gf, other, evt);
+    }
+    void onFileChanged(GFileMonitor* mon, GFile* gf, GFile* other, GFileMonitorEvent evt);
+
+private:
+    FilePath file;
+    GObjectPtr<GFileMonitor> mon;
+    std::vector<std::shared_ptr<const BookmarkItem>> items_;
+    static std::weak_ptr<Bookmarks> globalInstance_;
+    bool idle_handler;
+};
+
+} // namespace Fm
+
+#endif // FM2_BOOKMARKS_H
diff --git a/src/core/cstrptr.h b/src/core/cstrptr.h
new file mode 100644 (file)
index 0000000..5c06f0a
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef FM2_CSTRPTR_H
+#define FM2_CSTRPTR_H
+
+#include <memory>
+#include <glib.h>
+
+namespace Fm {
+
+struct CStrDeleter {
+    void operator()(char* ptr) const {
+        g_free(ptr);
+    }
+};
+
+// smart pointer for C string (char*) which should be freed by free()
+typedef std::unique_ptr<char[], CStrDeleter> CStrPtr;
+
+struct CStrHash {
+    std::size_t operator()(const char* str) const {
+        return g_str_hash(str);
+    }
+};
+
+struct CStrEqual {
+    bool operator()(const char* str1, const char* str2) const {
+        return g_str_equal(str1, str2);
+    }
+};
+
+struct CStrVDeleter {
+    void operator()(char** ptr) const {
+        g_strfreev(ptr);
+    }
+};
+
+// smart pointer for C string array (char**) which should be freed by g_strfreev() of glib
+typedef std::unique_ptr<char*[], CStrVDeleter> CStrArrayPtr;
+
+
+} // namespace Fm
+
+#endif // FM2_CSTRPTR_H
diff --git a/src/core/deletejob.cpp b/src/core/deletejob.cpp
new file mode 100644 (file)
index 0000000..6c3c550
--- /dev/null
@@ -0,0 +1,158 @@
+#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
new file mode 100644 (file)
index 0000000..8ad2321
--- /dev/null
@@ -0,0 +1,33 @@
+#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
new file mode 100644 (file)
index 0000000..abecff9
--- /dev/null
@@ -0,0 +1,180 @@
+#include "dirlistjob.h"
+#include <gio/gio.h>
+#include "fileinfo_p.h"
+#include "gioptrs.h"
+#include <QDebug>
+
+namespace Fm {
+
+DirListJob::DirListJob(const FilePath& path, Flags _flags, const std::shared_ptr<const HashSet>& cutFilesHashSet):
+    dir_path{path}, flags{_flags}, cutFilesHashSet_{cutFilesHashSet} {
+}
+
+void DirListJob::exec() {
+    GErrorPtr err;
+    GFileInfoPtr dir_inf;
+    GFilePtr dir_gfile = dir_path.gfile();
+    // FIXME: these are hacks for search:/// URI implemented by libfm which contains some bugs
+    bool isFileSearch = dir_path.hasUriScheme("search");
+    if(isFileSearch) {
+        // NOTE: The GFile instance changes its URI during file enumeration (bad design).
+        // So we create a copy here to avoid channging the gfile stored in dir_path.
+        // FIXME: later we should refactor file search and remove this dirty hack.
+        dir_gfile = GFilePtr{g_file_dup(dir_gfile.get())};
+    }
+_retry:
+    err.reset();
+    dir_inf = GFileInfoPtr{
+        g_file_query_info(dir_gfile.get(), 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<std::mutex> lock{mutex_};
+        dir_fi = std::make_shared<FileInfo>(dir_inf, dir_path);
+    }
+
+    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<FileInfo>(inf, FilePath(), 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, err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_CANCELLED
+                       ? ErrorSeverity::MILD // may happen at Folder::reload()
+                       : ErrorSeverity::CRITICAL);
+    }
+
+    // qDebug() << "END LISTING:" << dir_path.toString().get();
+    if(!foundFiles.empty()) {
+        std::lock_guard<std::mutex> lock{mutex_};
+        files_.swap(foundFiles);
+    }
+}
+
+#if 0
+//FIXME: incremental..
+
+static gboolean emit_found_files(gpointer user_data) {
+    /* this callback is called from the main thread */
+    FmDirListJob* job = FM_DIR_LIST_JOB(user_data);
+    /* g_print("emit_found_files: %d\n", g_slist_length(job->files_to_add)); */
+
+    if(g_source_is_destroyed(g_main_current_source())) {
+        return FALSE;
+    }
+    g_signal_emit(job, signals[FILES_FOUND], 0, job->files_to_add);
+    g_slist_free_full(job->files_to_add, (GDestroyNotify)fm_file_info_unref);
+    job->files_to_add = nullptr;
+    job->delay_add_files_handler = 0;
+    return FALSE;
+}
+
+static gpointer queue_add_file(FmJob* fmjob, gpointer user_data) {
+    FmDirListJob* job = FM_DIR_LIST_JOB(fmjob);
+    FmFileInfo* file = FM_FILE_INFO(user_data);
+    /* this callback is called from the main thread */
+    /* g_print("queue_add_file: %s\n", fm_file_info_get_disp_name(file)); */
+    job->files_to_add = g_slist_prepend(job->files_to_add, fm_file_info_ref(file));
+    if(job->delay_add_files_handler == 0)
+        job->delay_add_files_handler = g_timeout_add_seconds_full(G_PRIORITY_LOW,
+                                       1, emit_found_files, g_object_ref(job), g_object_unref);
+    return nullptr;
+}
+
+void fm_dir_list_job_add_found_file(FmDirListJob* job, FmFileInfo* file) {
+    fm_file_info_list_push_tail(job->files, file);
+    if(G_UNLIKELY(job->emit_files_found)) {
+        fm_job_call_main_thread(FM_JOB(job), queue_add_file, file);
+    }
+}
+#endif
+
+} // namespace Fm
diff --git a/src/core/dirlistjob.h b/src/core/dirlistjob.h
new file mode 100644 (file)
index 0000000..e578521
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef FM2_DIRLISTJOB_H
+#define FM2_DIRLISTJOB_H
+
+#include "../libfmqtglobals.h"
+#include <mutex>
+#include "job.h"
+#include "filepath.h"
+#include "gobjectptr.h"
+#include "fileinfo.h"
+
+namespace Fm {
+
+class LIBFM_QT_API DirListJob : public Job {
+    Q_OBJECT
+public:
+    enum Flags {
+        FAST = 0,
+        DIR_ONLY = 1 << 0,
+        DETAILED = 1 << 1
+    };
+
+    explicit DirListJob(const FilePath& path, Flags flags, const std::shared_ptr<const HashSet>& cutFilesHashSet = nullptr);
+
+    FileInfoList& files() {
+        return files_;
+    }
+
+    void setIncremental(bool set);
+
+    bool incremental() const {
+        return emit_files_found;
+    }
+
+    FilePath dirPath() const {
+        std::lock_guard<std::mutex> lock{mutex_};
+        return dir_path;
+    }
+
+    std::shared_ptr<const FileInfo> dirInfo() const {
+        std::lock_guard<std::mutex> lock{mutex_};
+        return dir_fi;
+    }
+
+Q_SIGNALS:
+    void filesFound(FileInfoList& foundFiles);
+
+protected:
+
+    void exec() override;
+
+private:
+    mutable std::mutex mutex_;
+    FilePath dir_path;
+    Flags flags;
+    std::shared_ptr<const FileInfo> dir_fi;
+    FileInfoList files_;
+    const std::shared_ptr<const HashSet> cutFilesHashSet_;
+    bool emit_files_found;
+    // guint delay_add_files_handler;
+    // GSList* files_to_add;
+};
+
+} // namespace Fm
+
+#endif // FM2_DIRLISTJOB_H
diff --git a/src/core/filechangeattrjob.cpp b/src/core/filechangeattrjob.cpp
new file mode 100644 (file)
index 0000000..8ed2abb
--- /dev/null
@@ -0,0 +1,324 @@
+#include "filechangeattrjob.h"
+#include "totalsizejob.h"
+
+#include <sys/stat.h>
+
+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
new file mode 100644 (file)
index 0000000..3f15863
--- /dev/null
@@ -0,0 +1,145 @@
+#ifndef FM2_FILECHANGEATTRJOB_H
+#define FM2_FILECHANGEATTRJOB_H
+
+#include "../libfmqtglobals.h"
+#include "fileoperationjob.h"
+#include "gioptrs.h"
+
+#include <string>
+
+#include <sys/types.h>
+
+namespace Fm {
+
+class LIBFM_QT_API FileChangeAttrJob : public Fm::FileOperationJob {
+    Q_OBJECT
+public:
+    explicit FileChangeAttrJob(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
new file mode 100644 (file)
index 0000000..41314f6
--- /dev/null
@@ -0,0 +1,451 @@
+#include "fileinfo.h"
+#include "fileinfo_p.h"
+#include <gio/gio.h>
+
+namespace Fm {
+
+const char defaultGFileInfoQueryAttribs[] = "standard::*,"
+                                            "unix::*,"
+                                            "time::*,"
+                                            "access::*,"
+                                            "id::filesystem,"
+                                            "metadata::emblems,"
+                                            "metadata::trust";
+
+FileInfo::FileInfo() {
+    // FIXME: initialize numeric data members
+}
+
+FileInfo::FileInfo(const GFileInfoPtr& inf, const FilePath& filePath, const FilePath& parentDirPath) {
+    setFromGFileInfo(inf, filePath, parentDirPath);
+}
+
+FileInfo::~FileInfo() {
+}
+
+void FileInfo::setFromGFileInfo(const GObjectPtr<GFileInfo>& inf, const FilePath& filePath, const FilePath& parentDirPath) {
+    inf_ = inf;
+    filePath_ = filePath;
+    if (filePath_ && filePath_.hasParent()) {
+        dirPath_ = filePath_.parent();
+    }
+    else {
+        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) {
+        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_ = (type == G_FILE_TYPE_SHORTCUT);
+    isMountable_ = (type == G_FILE_TYPE_MOUNTABLE);
+
+    /* 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:
+    /* 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(!mimeType_) {
+        mimeType_ = MimeType::fromName("application/octet-stream");
+    }
+
+    /* 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<const HashSet>& cutFilesHashSet) {
+    cutFilesHashSet_ = cutFilesHashSet;
+}
+
+bool FileInfo::canThumbnail() const {
+    /* We cannot use S_ISREG here as this exclude all symlinks */
+    if(size_ == 0 ||  /* don't generate thumbnails for empty files */
+            !(mode_ & S_IFREG) ||
+            isDesktopEntry() ||
+            isUnknownType()) {
+        return false;
+    }
+    return true;
+}
+
+/* full path of the file is required by this function */
+bool FileInfo::isExecutableType() const {
+    if(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 FileInfo::isTrustable() const {
+    if(isExecutableType()) {
+        /* to avoid GIO assertion warning: */
+        if(g_file_info_get_attribute_type(inf_.get(), "metadata::trust") == G_FILE_ATTRIBUTE_TYPE_STRING) {
+            if(const auto data = g_file_info_get_attribute_string(inf_.get(), "metadata::trust")) {
+                return (strcmp(data, "true") == 0);
+            }
+        }
+    }
+    return false;
+}
+
+void FileInfo::setTrustable(bool trust) const {
+    if(!isExecutableType()) {
+        return; // "metadata::trust" is only for executables
+    }
+    GObjectPtr<GFileInfo> info {g_file_info_new()}; // used to set only this attribute
+    if(trust) {
+        g_file_info_set_attribute_string(info.get(), "metadata::trust", "true");
+        g_file_info_set_attribute_string(inf_.get(), "metadata::trust", "true");
+    }
+    else {
+        g_file_info_set_attribute(info.get(), "metadata::trust", G_FILE_ATTRIBUTE_TYPE_INVALID, nullptr);
+        g_file_info_set_attribute(inf_.get(), "metadata::trust", G_FILE_ATTRIBUTE_TYPE_INVALID, nullptr);
+    }
+    g_file_set_attributes_from_info(path().gfile().get(),
+                                    info.get(),
+                                    G_FILE_QUERY_INFO_NONE,
+                                    nullptr, nullptr);
+}
+
+
+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
new file mode 100644 (file)
index 0000000..2a2d134
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2016 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __LIBFM_QT_FM2_FILE_INFO_H__
+#define __LIBFM_QT_FM2_FILE_INFO_H__
+
+#include <QObject>
+#include <QtGlobal>
+#include "../libfmqtglobals.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <vector>
+#include <set>
+#include <utility>
+#include <string>
+#include <forward_list>
+
+#include "gioptrs.h"
+#include "filepath.h"
+#include "iconinfo.h"
+#include "mimetype.h"
+
+
+namespace Fm {
+
+class FileInfoList;
+typedef std::set<unsigned int> HashSet;
+
+class LIBFM_QT_API FileInfo {
+public:
+
+    explicit FileInfo();
+
+    explicit FileInfo(const GFileInfoPtr& inf, const FilePath& filePath, const FilePath& parentDirPath = FilePath());
+
+    virtual ~FileInfo();
+
+    bool canSetHidden() const {
+        return isHiddenChangeable_;
+    }
+
+    bool canSetIcon() const {
+        return isIconChangeable_;
+    }
+
+    bool canSetName() const {
+        return isNameChangeable_;
+    }
+
+    bool canThumbnail() const;
+
+    gid_t gid() const {
+        return gid_;
+    }
+
+    uid_t uid() const {
+        return uid_;
+    }
+
+    const char* filesystemId() const {
+        return filesystemId_;
+    }
+
+    const std::shared_ptr<const IconInfo>& icon() const {
+        return icon_;
+    }
+
+    const std::shared_ptr<const MimeType>& mimeType() const {
+        return mimeType_;
+    }
+
+    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 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 filePath_ ? filePath_ : dirPath_ ? dirPath_.child(name_.c_str()) : FilePath::fromPathStr(name_.c_str());
+    }
+
+    const FilePath& dirPath() const {
+        return dirPath_;
+    }
+
+    void setFromGFileInfo(const GFileInfoPtr& inf, const FilePath& filePath, const FilePath& parentDirPath);
+
+    void bindCutFiles(const std::shared_ptr<const HashSet>& cutFilesHashSet);
+
+    const std::forward_list<std::shared_ptr<const IconInfo>>& emblems() const {
+        return emblems_;
+    }
+
+    bool isTrustable() const;
+
+    void setTrustable(bool trust) const;
+
+private:
+    GObjectPtr<GFileInfo> inf_;
+    std::string name_;
+    QString dispName_;
+
+    FilePath filePath_;
+    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<const MimeType> mimeType_;
+    std::shared_ptr<const IconInfo> icon_;
+    std::forward_list<std::shared_ptr<const IconInfo>> emblems_;
+
+    std::string target_; /* target of shortcut or mountable. */
+
+    bool isShortcut_ : 1; /* TRUE if file is shortcut type */
+    bool isMountable_ : 1; /* TRUE if file is mountable type */
+    bool isAccessible_ : 1; /* TRUE if can be read by user */
+    bool isWritable_ : 1; /* TRUE if can be written to by user */
+    bool isDeletable_ : 1; /* TRUE if can be deleted by user */
+    bool isHidden_ : 1; /* TRUE if file is hidden */
+    bool isBackup_ : 1; /* TRUE if file is backup */
+    bool isNameChangeable_ : 1; /* TRUE if name can be changed */
+    bool isIconChangeable_ : 1; /* TRUE if icon can be changed */
+    bool isHiddenChangeable_ : 1; /* TRUE if hidden can be changed */
+    bool isReadOnly_ : 1; /* TRUE if host FS is R/O */
+
+    std::weak_ptr<const HashSet> cutFilesHashSet_;
+    // std::vector<std::tuple<int, void*, void(void*)>> extraData_;
+};
+
+
+class LIBFM_QT_API FileInfoList: public std::vector<std::shared_ptr<const FileInfo>> {
+public:
+
+    bool isSameType() const;
+
+    bool isSameFilesystem() const;
+
+    FilePathList paths() const {
+        FilePathList ret;
+        for(auto& file: *this) {
+            ret.push_back(file->path());
+        }
+        return ret;
+    }
+};
+
+// smart pointer to FileInfo objects (once created, FileInfo objects should stay immutable so const is needed here)
+typedef std::shared_ptr<const FileInfo> FileInfoPtr;
+
+typedef std::pair<FileInfoPtr, FileInfoPtr> FileInfoPair;
+
+}
+
+Q_DECLARE_METATYPE(std::shared_ptr<const Fm::FileInfo>)
+
+
+#endif // __LIBFM_QT_FM2_FILE_INFO_H__
diff --git a/src/core/fileinfo_p.h b/src/core/fileinfo_p.h
new file mode 100644 (file)
index 0000000..f7a51a6
--- /dev/null
@@ -0,0 +1,10 @@
+#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
new file mode 100644 (file)
index 0000000..7742011
--- /dev/null
@@ -0,0 +1,50 @@
+#include "fileinfojob.h"
+#include "fileinfo_p.h"
+
+namespace Fm {
+
+FileInfoJob::FileInfoJob(FilePathList paths, const std::shared_ptr<const HashSet>& cutFilesHashSet):
+    Job(),
+    paths_{std::move(paths)},
+    cutFilesHashSet_{cutFilesHashSet} {
+}
+
+void FileInfoJob::exec() {
+    for(const auto& path: paths_) {
+        if(isCancelled()) {
+            break;
+        }
+        currentPath_ = path;
+
+        bool retry;
+        do {
+            retry = false;
+            GErrorPtr err;
+            GFileInfoPtr inf{
+                g_file_query_info(path.gfile().get(), defaultGFileInfoQueryAttribs,
+                                  G_FILE_QUERY_INFO_NONE, cancellable().get(), &err),
+                false
+            };
+            if(inf) {
+                auto fileInfoPtr = std::make_shared<FileInfo>(inf, path);
+
+                // FIXME: this is not elegant
+                if(cutFilesHashSet_
+                        && cutFilesHashSet_->count(path.hash())) {
+                    fileInfoPtr->bindCutFiles(cutFilesHashSet_);
+                }
+
+                results_.push_back(fileInfoPtr);
+                Q_EMIT gotInfo(path, results_.back());
+            }
+            else {
+                auto act = emitError(err);
+                if(act == Job::ErrorAction::RETRY) {
+                    retry = true;
+                }
+            }
+        } while(retry && !isCancelled());
+    }
+}
+
+} // namespace Fm
diff --git a/src/core/fileinfojob.h b/src/core/fileinfojob.h
new file mode 100644 (file)
index 0000000..374a7f1
--- /dev/null
@@ -0,0 +1,45 @@
+#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, const std::shared_ptr<const HashSet>& cutFilesHashSet = nullptr);
+
+    const FilePathList& paths() const {
+        return paths_;
+    }
+
+    const FileInfoList& files() const {
+        return results_;
+    }
+
+    const FilePath& currentPath() const {
+        return currentPath_;
+    }
+
+Q_SIGNALS:
+    void gotInfo(const FilePath& path, std::shared_ptr<const FileInfo>& info);
+
+protected:
+    void exec() override;
+
+private:
+    FilePathList paths_;
+    FileInfoList results_;
+    const std::shared_ptr<const HashSet> cutFilesHashSet_;
+    FilePath currentPath_;
+};
+
+} // namespace Fm
+
+#endif // FM2_FILEINFOJOB_H
diff --git a/src/core/filelinkjob.cpp b/src/core/filelinkjob.cpp
new file mode 100644 (file)
index 0000000..253768e
--- /dev/null
@@ -0,0 +1,9 @@
+#include "filelinkjob.h"
+
+namespace Fm {
+
+FileLinkJob::FileLinkJob() {
+
+}
+
+} // namespace Fm
diff --git a/src/core/filelinkjob.h b/src/core/filelinkjob.h
new file mode 100644 (file)
index 0000000..965d089
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef FM2_FILELINKJOB_H
+#define FM2_FILELINKJOB_H
+
+#include "../libfmqtglobals.h"
+#include "fileoperationjob.h"
+
+namespace Fm {
+
+class LIBFM_QT_API FileLinkJob : public Fm::FileOperationJob {
+public:
+    explicit FileLinkJob();
+};
+
+} // namespace Fm
+
+#endif // FM2_FILELINKJOB_H
diff --git a/src/core/filemonitor.cpp b/src/core/filemonitor.cpp
new file mode 100644 (file)
index 0000000..68c6b6b
--- /dev/null
@@ -0,0 +1,9 @@
+#include "filemonitor.h"
+
+namespace Fm {
+
+FileMonitor::FileMonitor() {
+
+}
+
+} // namespace Fm
diff --git a/src/core/filemonitor.h b/src/core/filemonitor.h
new file mode 100644 (file)
index 0000000..9a1485d
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef FM2_FILEMONITOR_H
+#define FM2_FILEMONITOR_H
+
+#include "../libfmqtglobals.h"
+#include <QObject>
+#include "gioptrs.h"
+#include "filepath.h"
+
+namespace Fm {
+
+class LIBFM_QT_API FileMonitor: public QObject {
+    Q_OBJECT
+public:
+
+    explicit FileMonitor();
+
+Q_SIGNALS:
+
+
+private:
+    GFileMonitorPtr monitor_;
+};
+
+} // namespace Fm
+
+#endif // FM2_FILEMONITOR_H
diff --git a/src/core/fileoperationjob.cpp b/src/core/fileoperationjob.cpp
new file mode 100644 (file)
index 0000000..c2fbb26
--- /dev/null
@@ -0,0 +1,102 @@
+#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<std::mutex> lock{mutex_};
+    if(hasTotalAmount_) {
+        fileSize = totalSize_;
+        fileCount = totalCount_;
+    }
+    return hasTotalAmount_;
+}
+
+bool FileOperationJob::currentFileProgress(FilePath& path, uint64_t& totalSize, uint64_t& finishedSize) const {
+    std::lock_guard<std::mutex> lock{mutex_};
+    if(currentFile_.isValid()) {
+        path = currentFile_;
+        totalSize = currentFileSize_;
+        finishedSize = currentFileFinished_;
+    }
+    return currentFile_.isValid();
+}
+
+double FileOperationJob::progress() const {
+    std::lock_guard<std::mutex> lock{mutex_};
+    double finishedRatio;
+    if(calcProgressUsingSize_) {
+        finishedRatio = totalSize_ > 0 ? double(finishedSize_ + currentFileFinished_) / totalSize_ : 0.0;
+    }
+    else {
+        finishedRatio = totalCount_ > 0 ? double(finishedCount_) / totalCount_ : 0.0;
+    }
+
+    if(finishedRatio > 1.0) {
+        finishedRatio = 1.0;
+    }
+    return finishedRatio;
+}
+
+FileOperationJob::FileExistsAction FileOperationJob::askRename(const FileInfo &src, const FileInfo &dest, FilePath &newDest) {
+    FileExistsAction action = SKIP;
+    Q_EMIT fileExists(src, dest, action, newDest);
+    return action;
+}
+
+bool FileOperationJob::finishedAmount(uint64_t& finishedSize, uint64_t& finishedCount) const {
+    std::lock_guard<std::mutex> lock{mutex_};
+    if(hasTotalAmount_) {
+        finishedSize = finishedSize_;
+        finishedCount = finishedCount_;
+    }
+    return hasTotalAmount_;
+}
+
+void FileOperationJob::setTotalAmount(uint64_t fileSize, uint64_t fileCount) {
+    std::lock_guard<std::mutex> lock{mutex_};
+    hasTotalAmount_ = true;
+    totalSize_ = fileSize;
+    totalCount_ = fileCount;
+}
+
+void FileOperationJob::setFinishedAmount(uint64_t finishedSize, uint64_t finishedCount) {
+    std::lock_guard<std::mutex> lock{mutex_};
+    finishedSize_ = finishedSize;
+    finishedCount_ = finishedCount;
+}
+
+void FileOperationJob::addFinishedAmount(uint64_t finishedSize, uint64_t finishedCount) {
+    std::lock_guard<std::mutex> lock{mutex_};
+    finishedSize_ += finishedSize;
+    finishedCount_ += finishedCount;
+}
+
+FilePath FileOperationJob::currentFile() const {
+    std::lock_guard<std::mutex> lock{mutex_};
+    auto ret = currentFile_;
+    return ret;
+}
+
+void FileOperationJob::setCurrentFile(const FilePath& path) {
+    std::lock_guard<std::mutex> lock{mutex_};
+    currentFile_ = path;
+}
+
+void FileOperationJob::setCurrentFileProgress(uint64_t totalSize, uint64_t finishedSize) {
+    std::lock_guard<std::mutex> lock{mutex_};
+    currentFileSize_ = totalSize;
+    currentFileFinished_ = finishedSize;
+}
+
+} // namespace Fm
diff --git a/src/core/fileoperationjob.h b/src/core/fileoperationjob.h
new file mode 100644 (file)
index 0000000..f491a4b
--- /dev/null
@@ -0,0 +1,96 @@
+#ifndef FM2_FILEOPERATIONJOB_H
+#define FM2_FILEOPERATIONJOB_H
+
+#include "../libfmqtglobals.h"
+#include "job.h"
+#include <string>
+#include <mutex>
+#include <cstdint>
+#include "fileinfo.h"
+#include "filepath.h"
+
+namespace Fm {
+
+class LIBFM_QT_API FileOperationJob : public Fm::Job {
+    Q_OBJECT
+public:
+    enum FileExistsAction {
+        CANCEL = 0,
+        OVERWRITE = 1<<0,
+        RENAME = 1<<1,
+        SKIP = 1<<2,
+        SKIP_ERROR = 1<<3
+    };
+
+    explicit FileOperationJob();
+
+    // 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
new file mode 100644 (file)
index 0000000..36a7910
--- /dev/null
@@ -0,0 +1,21 @@
+#include "filepath.h"
+#include <cstdlib>
+#include <utility>
+#include <glib.h>
+
+namespace Fm {
+
+FilePath FilePath::homeDir_;
+
+const FilePath &FilePath::homeDir() {
+    if(!homeDir_) {
+        const char* home = getenv("HOME");
+        if(!home) {
+            home = g_get_home_dir();
+        }
+        homeDir_ = FilePath::fromLocalPath(home);
+    }
+    return homeDir_;
+}
+
+} // namespace Fm
diff --git a/src/core/filepath.h b/src/core/filepath.h
new file mode 100644 (file)
index 0000000..a0ce899
--- /dev/null
@@ -0,0 +1,177 @@
+#ifndef FM2_FILEPATH_H
+#define FM2_FILEPATH_H
+
+#include "../libfmqtglobals.h"
+#include "gobjectptr.h"
+#include "cstrptr.h"
+#include <gio/gio.h>
+#include <vector>
+#include <QMetaType>
+
+namespace Fm {
+
+class LIBFM_QT_API FilePath {
+public:
+
+    explicit FilePath() {
+    }
+
+    explicit FilePath(GFile* gfile, bool add_ref): gfile_{gfile, add_ref} {
+    }
+
+    FilePath(const FilePath& other): FilePath{} {
+        *this = other;
+    }
+
+    FilePath(FilePath&& other) noexcept: FilePath{} {
+        *this = other;
+    }
+
+    static FilePath fromUri(const char* uri) {
+        return FilePath{g_file_new_for_uri(uri), false};
+    }
+
+    static FilePath fromLocalPath(const char* path) {
+        return FilePath{g_file_new_for_path(path), false};
+    }
+
+    static FilePath fromDisplayName(const char* path) {
+        return FilePath{g_file_parse_name(path), false};
+    }
+
+    static FilePath fromPathStr(const char* path_str) {
+        return FilePath{g_file_new_for_commandline_arg(path_str), false};
+    }
+
+    bool isValid() const {
+        return gfile_ != nullptr;
+    }
+
+    unsigned int hash() const {
+        return g_file_hash(gfile_.get());
+    }
+
+    CStrPtr baseName() const {
+        return CStrPtr{g_file_get_basename(gfile_.get())};
+    }
+
+    CStrPtr localPath() const {
+        return CStrPtr{g_file_get_path(gfile_.get())};
+    }
+
+    CStrPtr uri() const {
+        return CStrPtr{g_file_get_uri(gfile_.get())};
+    }
+
+    CStrPtr toString() const {
+        if(isNative()) {
+            return localPath();
+        }
+        return uri();
+    }
+
+    // a human readable UTF-8 display name for the path
+    CStrPtr displayName() const {
+        return CStrPtr{g_file_get_parse_name(gfile_.get())};
+    }
+
+    FilePath parent() const {
+        return FilePath{g_file_get_parent(gfile_.get()), false};
+    }
+
+    bool hasParent() const {
+        return g_file_has_parent(gfile_.get(), nullptr);
+    }
+
+    bool isParentOf(const FilePath& other) const {
+        return g_file_has_parent(other.gfile_.get(), gfile_.get());
+    }
+
+    bool isPrefixOf(const FilePath& other) const {
+        return g_file_has_prefix(other.gfile_.get(), gfile_.get());
+    }
+
+    FilePath child(const char* name) const {
+        return FilePath{g_file_get_child(gfile_.get(), name), false};
+    }
+
+    CStrPtr relativePathStr(const FilePath& descendant) const {
+        return CStrPtr{g_file_get_relative_path(gfile_.get(), descendant.gfile_.get())};
+    }
+
+    FilePath relativePath(const char* relPath) const {
+        return FilePath{g_file_resolve_relative_path(gfile_.get(), relPath), false};
+    }
+
+    bool isNative() const {
+        return g_file_is_native(gfile_.get());
+    }
+
+    bool hasUriScheme(const char* scheme) const {
+        return g_file_has_uri_scheme(gfile_.get(), scheme);
+    }
+
+    CStrPtr uriScheme() const {
+        return CStrPtr{g_file_get_uri_scheme(gfile_.get())};
+    }
+
+    const GObjectPtr<GFile>& gfile() const {
+        return gfile_;
+    }
+
+    FilePath& operator = (const FilePath& other) {
+        gfile_ = other.gfile_;
+        return *this;
+    }
+
+    FilePath& operator = (const FilePath&& other) noexcept {
+        gfile_ = std::move(other.gfile_);
+        return *this;
+    }
+
+    bool operator == (const FilePath& other) const {
+        return operator==(other.gfile_.get());
+    }
+
+    bool operator == (GFile* other_gfile) const {
+        if(gfile_ == other_gfile) {
+            return true;
+        }
+        if(gfile_ && other_gfile) {
+            return g_file_equal(gfile_.get(), other_gfile);
+        }
+        return false;
+    }
+
+    bool operator != (const FilePath& other) const {
+        return !operator==(other);
+    }
+
+    bool operator != (std::nullptr_t) const {
+        return gfile_ != nullptr;
+    }
+
+    operator bool() const {
+        return gfile_ != nullptr;
+    }
+
+    static const FilePath& homeDir();
+
+private:
+    GObjectPtr<GFile> gfile_;
+    static FilePath homeDir_;
+};
+
+struct FilePathHash {
+    std::size_t operator() (const FilePath& path) const {
+        return path.hash();
+    }
+};
+
+typedef std::vector<FilePath> FilePathList;
+
+} // namespace Fm
+
+Q_DECLARE_METATYPE(Fm::FilePath)
+
+#endif // FM2_FILEPATH_H
diff --git a/src/core/filesysteminfojob.cpp b/src/core/filesysteminfojob.cpp
new file mode 100644 (file)
index 0000000..6c3aa69
--- /dev/null
@@ -0,0 +1,24 @@
+#include "filesysteminfojob.h"
+#include "gobjectptr.h"
+
+namespace Fm {
+
+void FileSystemInfoJob::exec() {
+    GObjectPtr<GFileInfo> inf = GObjectPtr<GFileInfo>{
+            g_file_query_filesystem_info(
+                path_.gfile().get(),
+                G_FILE_ATTRIBUTE_FILESYSTEM_SIZE","
+                G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
+                cancellable().get(), nullptr),
+            false
+    };
+    if(!inf)
+        return;
+    if(g_file_info_has_attribute(inf.get(), G_FILE_ATTRIBUTE_FILESYSTEM_SIZE)) {
+        size_ = g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_FILESYSTEM_SIZE);
+        freeSize_ = g_file_info_get_attribute_uint64(inf.get(), G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
+        isAvailable_ = true;
+    }
+}
+
+} // namespace Fm
diff --git a/src/core/filesysteminfojob.h b/src/core/filesysteminfojob.h
new file mode 100644 (file)
index 0000000..59c963a
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef FM2_FILESYSTEMINFOJOB_H
+#define FM2_FILESYSTEMINFOJOB_H
+
+#include "../libfmqtglobals.h"
+#include "job.h"
+#include "filepath.h"
+
+namespace Fm {
+
+class LIBFM_QT_API FileSystemInfoJob : public Job {
+    Q_OBJECT
+public:
+    explicit FileSystemInfoJob(const FilePath& path):
+        path_{path},
+        isAvailable_{false},
+        size_{0},
+        freeSize_{0} {
+    }
+
+    bool isAvailable() const {
+        return isAvailable_;
+    }
+
+    uint64_t size() const {
+        return size_;
+    }
+
+    uint64_t freeSize() const {
+        return freeSize_;
+    }
+
+protected:
+
+    void exec() override;
+
+private:
+    FilePath path_;
+    bool isAvailable_;
+    uint64_t size_;
+    uint64_t freeSize_;
+};
+
+} // namespace Fm
+
+#endif // FM2_FILESYSTEMINFOJOB_H
diff --git a/src/core/filetransferjob.cpp b/src/core/filetransferjob.cpp
new file mode 100644 (file)
index 0000000..4039c58
--- /dev/null
@@ -0,0 +1,652 @@
+#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}, FileInfo{destInfo, destPath}, 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},
+                                             FileInfo{destInfo, destPath},
+                                             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:
+            // prevent a dir from being copied into itself
+            if(srcPath.isPrefixOf(destPath)) {
+                GErrorPtr err = GErrorPtr{
+                                G_IO_ERROR,
+                                G_IO_ERROR_NOT_SUPPORTED,
+                                tr("Cannot copy a directory into itself!")
+                };
+                emitError(err, ErrorSeverity::MODERATE);
+            }
+            else {
+                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
new file mode 100644 (file)
index 0000000..7c3d8a0
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef FM2_COPYJOB_H
+#define FM2_COPYJOB_H
+
+#include "../libfmqtglobals.h"
+#include "fileoperationjob.h"
+#include "gioptrs.h"
+
+namespace Fm {
+
+class LIBFM_QT_API FileTransferJob : public Fm::FileOperationJob {
+    Q_OBJECT
+public:
+
+    enum class Mode {
+        COPY,
+        MOVE,
+        LINK
+    };
+
+    explicit FileTransferJob(FilePathList srcPaths, Mode mode = Mode::COPY);
+    explicit FileTransferJob(FilePathList srcPaths, FilePathList destPaths, Mode mode = Mode::COPY);
+    explicit FileTransferJob(FilePathList srcPaths, const FilePath &destDirPath, Mode mode = Mode::COPY);
+
+    void setSrcPaths(FilePathList srcPaths);
+    void setDestPaths(FilePathList destPaths);
+    void setDestDirPath(const FilePath &destDirPath);
+
+protected:
+    void exec() override;
+
+private:
+    bool processPath(const FilePath& srcPath, const FilePath& destPath, const char *destFileName);
+    bool moveFile(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName);
+    bool copyFile(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName, bool skip = false);
+    bool linkFile(const FilePath &srcPath, const GFileInfoPtr &srcInfo, const FilePath &destDirPath, const char *destFileName);
+
+    bool moveFileSameFs(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath &destPath);
+    bool copyRegularFile(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath &destPath);
+    bool copySpecialFile(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath& destPath);
+    bool copyDirContent(const FilePath &srcPath, GFileInfoPtr srcInfo, FilePath &destPath, bool skip = false);
+    bool makeDir(const FilePath &srcPath, GFileInfoPtr srcInfo, FilePath &destPath);
+    bool createSymlink(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath& destPath);
+    bool createShortcut(const FilePath &srcPath, const GFileInfoPtr& srcInfo, FilePath& destPath);
+
+    bool handleError(GErrorPtr& err, const FilePath &srcPath, const GFileInfoPtr &srcInfo, FilePath &destPath, int& flags);
+
+    static void gfileCopyProgressCallback(goffset current_num_bytes, goffset total_num_bytes, FileTransferJob* _this);
+
+private:
+    FilePathList srcPaths_;
+    FilePathList destPaths_;
+    Mode mode_;
+};
+
+
+} // namespace Fm
+
+#endif // FM2_COPYJOB_H
diff --git a/src/core/folder.cpp b/src/core/folder.cpp
new file mode 100644 (file)
index 0000000..813b5ad
--- /dev/null
@@ -0,0 +1,938 @@
+/*
+ *      fm-folder.c
+ *
+ *      Copyright 2009 - 2012 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *      Copyright 2012-2016 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ *      This file is a part of the Libfm library.
+ *
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2.1 of the License, or (at your option) any later version.
+ *
+ *      This library is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *      Lesser General Public License for more details.
+ *
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "folder.h"
+#include <string.h>
+#include <cassert>
+#include <QTimer>
+#include <QDebug>
+
+#include "dirlistjob.h"
+#include "filesysteminfojob.h"
+#include "fileinfojob.h"
+
+namespace Fm {
+
+std::unordered_map<FilePath, std::weak_ptr<Folder>, FilePathHash> Folder::cache_;
+QString Folder::cutFilesDirPath_;
+QString Folder::lastCutFilesDirPath_;
+std::shared_ptr<const HashSet> Folder::cutFilesHashSet_;
+std::mutex Folder::mutex_;
+
+Folder::Folder():
+    dirlist_job{nullptr},
+    fsInfoJob_{nullptr},
+    volumeManager_{VolumeManager::globalInstance()},
+    /* for file monitor */
+    has_idle_reload_handler{0},
+    has_idle_update_handler{false},
+    pending_change_notify{false},
+    filesystem_info_pending{false},
+    wants_incremental{false},
+    stop_emission{false}, /* don't set it 1 bit to not lock other bits */
+    /* filesystem info - set in query thread, read in main */
+    fs_total_size{0},
+    fs_free_size{0},
+    has_fs_info{false},
+    defer_content_test{false} {
+
+    connect(volumeManager_.get(), &VolumeManager::mountAdded, this, &Folder::onMountAdded);
+    connect(volumeManager_.get(), &VolumeManager::mountRemoved, this, &Folder::onMountRemoved);
+}
+
+Folder::Folder(const FilePath& path): Folder() {
+    dirPath_ = path;
+}
+
+Folder::~Folder() {
+    if(dirMonitor_) {
+        g_signal_handlers_disconnect_by_data(dirMonitor_.get(), this);
+        dirMonitor_.reset();
+    }
+
+    if(dirlist_job) {
+        dirlist_job->cancel();
+    }
+
+    // cancel any file info job in progress.
+    for(auto job: fileinfoJobs_) {
+        job->cancel();
+    }
+
+    if(fsInfoJob_) {
+        fsInfoJob_->cancel();
+    }
+
+    // We store a weak_ptr instead of shared_ptr in the hash table, so the hash table
+    // does not own a reference to the folder. When the last reference to Folder is
+    // freed, we need to remove its hash table entry.
+    std::lock_guard<std::mutex> lock{mutex_};
+    auto it = cache_.find(dirPath_);
+    if(it != cache_.end()) {
+        cache_.erase(it);
+    }
+}
+
+// static
+std::shared_ptr<Folder> Folder::fromPath(const FilePath& path) {
+    std::lock_guard<std::mutex> lock{mutex_};
+    auto it = cache_.find(path);
+    if(it != cache_.end()) {
+        auto folder = it->second.lock();
+        if(folder) {
+            return folder;
+        }
+        else { // FIXME: is this possible?
+            cache_.erase(it);
+        }
+    }
+    auto folder = std::make_shared<Folder>(path);
+    folder->reload();
+    cache_.emplace(path, folder);
+    return folder;
+}
+
+bool Folder::makeDirectory(const char* /*name*/, GError** /*error*/) {
+    // TODO:
+    // FIXME: what the API is used for in the original libfm C API?
+    return false;
+}
+
+bool Folder::isIncremental() const {
+    return wants_incremental;
+}
+
+bool Folder::isValid() const {
+    return dirInfo_ != nullptr;
+}
+
+bool Folder::isLoaded() const {
+    return (dirlist_job == nullptr);
+}
+
+std::shared_ptr<const FileInfo> Folder::fileByName(const char* name) const {
+    auto it = files_.find(name);
+    if(it != files_.end()) {
+        return it->second;
+    }
+    return nullptr;
+}
+
+bool Folder::isEmpty() const {
+    return files_.empty();
+}
+
+FileInfoList Folder::files() const {
+    FileInfoList ret;
+    ret.reserve(files_.size());
+    for(const auto& item : files_) {
+        ret.push_back(item.second);
+    }
+    return ret;
+}
+
+
+const FilePath& Folder::path() const {
+    auto pathStr = dirPath_.toString();
+    // qDebug() << this << "FOLDER_PATH:" << pathStr.get() << dirPath_.gfile().get();
+    //assert(!g_str_has_prefix(pathStr.get(), "file:"));
+    return dirPath_;
+}
+
+const std::shared_ptr<const FileInfo>& Folder::info() const {
+    return dirInfo_;
+}
+
+#if 0
+void Folder::init(FmFolder* folder) {
+    files = fm_file_info_list_new();
+    G_LOCK(hash);
+    if(G_UNLIKELY(hash_uses == 0)) {
+        hash = g_hash_table_new((GHashFunc)fm_path_hash, (GEqualFunc)fm_path_equal);
+        volume_monitor = g_volume_monitor_get();
+        if(G_LIKELY(volume_monitor)) {
+            g_signal_connect(volume_monitor, "mount-added", G_CALLBACK(on_mount_added), nullptr);
+            g_signal_connect(volume_monitor, "mount-removed", G_CALLBACK(on_mount_removed), nullptr);
+        }
+    }
+    hash_uses++;
+    G_UNLOCK(hash);
+}
+#endif
+
+void Folder::onIdleReload() {
+    /* check if folder still exists */
+    reload();
+    // G_LOCK(query);
+    has_idle_reload_handler = false;
+    // G_UNLOCK(query);
+}
+
+void Folder::queueReload() {
+    // G_LOCK(query);
+    if(!has_idle_reload_handler) {
+        has_idle_reload_handler = true;
+        QTimer::singleShot(0, this, &Folder::onIdleReload);
+    }
+    // G_UNLOCK(query);
+}
+
+void Folder::onFileInfoFinished() {
+    FileInfoJob* job = static_cast<FileInfoJob*>(sender());
+    fileinfoJobs_.erase(std::find(fileinfoJobs_.cbegin(), fileinfoJobs_.cend(), job));
+
+    /* NOTE: The pending changes should be processed in the order reported by GIO;
+       otherwise; the final file info might be wrong. Here, we ensure that the next
+       pending changes are processed only after the current info job is finished. */
+
+    if(job->isCancelled()) {
+        has_idle_update_handler = false; // allow future updates
+        return;
+    }
+
+    // process the changes accumulated during this info job
+    if(filesystem_info_pending // means a pending change; see "onFileSystemInfoFinished()"
+       || !paths_to_update.empty() || !paths_to_add.empty() || !paths_to_del.empty()) {
+        QTimer::singleShot(0, this, &Folder::processPendingChanges);
+    }
+    // there's no pending change at the moment; let the next one be processed
+    else {
+        has_idle_update_handler = false;
+    }
+
+    FileInfoList files_to_add;
+    FileInfoList files_to_delete;
+    std::vector<FileInfoPair> files_to_update;
+
+    const auto& paths = job->paths();
+    const auto& infos = job->files();
+    auto path_it = paths.cbegin();
+    auto info_it = infos.cbegin();
+    for(; path_it != paths.cend() && info_it != infos.cend(); ++path_it, ++info_it) {
+        const auto& path = *path_it;
+        const auto& info = *info_it;
+
+        if(path == dirPath_) { // got the info for the folder itself.
+            dirInfo_ = info;
+        }
+        else {
+            auto it = files_.find(info->path().baseName().get());
+            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->path().baseName().get()] = info;
+        }
+    }
+    if(!files_to_add.empty()) {
+        Q_EMIT filesAdded(files_to_add);
+    }
+    if(!files_to_update.empty()) {
+        Q_EMIT filesChanged(files_to_update);
+    }
+    Q_EMIT contentChanged();
+}
+
+void Folder::processPendingChanges() {
+    // FmFileInfoJob* job = nullptr;
+    std::lock_guard<std::mutex> lock{mutex_};
+
+    // idle_handler = 0;
+    /* if we were asked to block updates let delay it for now */
+    if(stop_emission) {
+        has_idle_update_handler = false;
+        return;
+    }
+
+    FileInfoJob* info_job = nullptr;
+    if(!paths_to_update.empty() || !paths_to_add.empty()) {
+        FilePathList paths;
+        paths.insert(paths.end(), paths_to_add.cbegin(), paths_to_add.cend());
+        paths.insert(paths.end(), paths_to_update.cbegin(), paths_to_update.cend());
+        info_job = new FileInfoJob{paths, hasCutFiles() ? cutFilesHashSet_ : nullptr};
+        paths_to_update.clear();
+        paths_to_add.clear();
+    }
+    else {
+        // let the next pending changes be processed; see "onFileInfoFinished()"
+        has_idle_update_handler = false;
+    }
+
+    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
+    }
+
+    // process deletion
+    if(!paths_to_del.empty()) {
+        FileInfoList deleted_files;
+        for(const auto &path: paths_to_del) {
+            auto name = path.baseName();
+            auto it = files_.find(name.get());
+            if(it != files_.end()) {
+                deleted_files.push_back(it->second);
+                files_.erase(it);
+            }
+        }
+        if(!deleted_files.empty()) {
+            Q_EMIT filesRemoved(deleted_files);
+            Q_EMIT contentChanged();
+        }
+        paths_to_del.clear();
+    }
+
+    if(pending_change_notify) {
+        Q_EMIT changed();
+        /* update volume info */
+        queryFilesystemInfo();
+        pending_change_notify = false;
+    }
+
+    if(filesystem_info_pending) {
+        Q_EMIT fileSystemChanged();
+        filesystem_info_pending = false;
+    }
+}
+
+/* should be called only with G_LOCK(lists) on! */
+void Folder::queueUpdate() {
+    // qDebug() << "queue_update:" << !has_idle_handler << paths_to_add.size() << paths_to_update.size() << paths_to_del.size();
+    if(!has_idle_update_handler) {
+        QTimer::singleShot(0, this, &Folder::processPendingChanges);
+        has_idle_update_handler = true;
+    }
+}
+
+
+/* NOTE: When queuing files for addition/update/deletion in the following functions,
+   the currently detected files (namely, "files_") should not be taken into account
+   because they might be changed soon due to a previous call to queueUpdate(). */
+
+/* returns true if reference was taken from path */
+bool Folder::eventFileAdded(const FilePath &path) {
+    bool added = true;
+    // G_LOCK(lists);
+    if(std::find(paths_to_del.cbegin(), paths_to_del.cend(), path) != paths_to_del.cend()) {
+        // if the file was going to be deleted, its addition means an update,
+        // so remove it from the deletion queue and add it to the update queue
+        paths_to_del.erase(std::remove(paths_to_del.begin(), paths_to_del.end(), path), paths_to_del.cend());
+        if(std::find(paths_to_update.cbegin(), paths_to_update.cend(), path) == paths_to_update.cend()) {
+            paths_to_update.push_back(path);
+        }
+    }
+    else if(std::find(paths_to_add.cbegin(), paths_to_add.cend(), path) == paths_to_add.cend()) {
+        paths_to_add.push_back(path);
+    }
+    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);
+    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()) {
+        paths_to_update.push_back(path);
+        added = true;
+        queueUpdate();
+    }
+    else {
+        added = false;
+    }
+    // G_UNLOCK(lists);
+    return added;
+}
+
+void Folder::eventFileDeleted(const FilePath& path) {
+    bool deleted = true;
+    // qDebug() << "delete " << path.baseName().get();
+    // G_LOCK(lists);
+    if(std::find(paths_to_add.cbegin(), paths_to_add.cend(), path) != paths_to_add.cend()) {
+        // if the file was going to be added, just remove it from the addition queue
+        paths_to_add.erase(std::remove(paths_to_add.begin(), paths_to_add.end(), path), paths_to_add.cend());
+    }
+    else if(std::find(paths_to_del.cbegin(), paths_to_del.cend(), path) == paths_to_del.cend()) {
+        paths_to_del.push_back(path);
+        // the update queue should be cancelled 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());
+    }
+    else {
+        deleted = false;
+    }
+
+    if(deleted) {
+        queueUpdate();
+    }
+    // G_UNLOCK(lists);
+}
+
+
+void Folder::onDirChanged(GFileMonitorEvent evt) {
+    switch(evt) {
+    case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
+        /* g_debug("folder is going to be unmounted"); */
+        break;
+    case G_FILE_MONITOR_EVENT_UNMOUNTED:
+        Q_EMIT unmount();
+        /* g_debug("folder is unmounted"); */
+        queueReload();
+        break;
+    case G_FILE_MONITOR_EVENT_DELETED:
+        Q_EMIT removed();
+        /* g_debug("folder is deleted"); */
+        break;
+    case G_FILE_MONITOR_EVENT_CREATED:
+        queueReload();
+        break;
+    case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
+    case G_FILE_MONITOR_EVENT_CHANGED: {
+        std::lock_guard<std::mutex> lock{mutex_};
+        pending_change_notify = true;
+        if(std::find(paths_to_update.cbegin(), paths_to_update.cend(), dirPath_) != paths_to_update.cend()) {
+            paths_to_update.push_back(dirPath_);
+            queueUpdate();
+        }
+        /* g_debug("folder is changed"); */
+        break;
+    }
+#if GLIB_CHECK_VERSION(2,24,0)
+    case G_FILE_MONITOR_EVENT_MOVED:
+#endif
+    case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+        ;
+    default:
+        break;
+    }
+}
+
+void Folder::onFileChangeEvents(GFileMonitor* /*monitor*/, GFile* gf, GFile* /*other_file*/, GFileMonitorEvent evt) {
+    /* const char* names[]={
+        "G_FILE_MONITOR_EVENT_CHANGED",
+        "G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT",
+        "G_FILE_MONITOR_EVENT_DELETED",
+        "G_FILE_MONITOR_EVENT_CREATED",
+        "G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED",
+        "G_FILE_MONITOR_EVENT_PRE_UNMOUNT",
+        "G_FILE_MONITOR_EVENT_UNMOUNTED"
+    }; */
+    if(dirPath_ == gf) {
+        onDirChanged(evt);
+        return;
+    }
+    else {
+        std::lock_guard<std::mutex> lock{mutex_};
+        auto path = FilePath{gf, true};
+        /* NOTE: sometimes, for unknown reasons, GFileMonitor gives us the
+         * same event of the same file for multiple times. So we need to
+         * check for duplications ourselves here. */
+        switch(evt) {
+        case G_FILE_MONITOR_EVENT_CREATED:
+            eventFileAdded(path);
+            break;
+        case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
+        case G_FILE_MONITOR_EVENT_CHANGED:
+            eventFileChanged(path);
+            break;
+        case G_FILE_MONITOR_EVENT_DELETED:
+            eventFileDeleted(path);
+            break;
+        default:
+            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<const HashSet>& cutFilesHashSet) {
+    if(cutFilesHashSet_ && !cutFilesHashSet_->empty()) {
+        lastCutFilesDirPath_ = cutFilesDirPath_;
+    }
+    cutFilesDirPath_ = dirPath_.toString().get();
+    cutFilesHashSet_ = cutFilesHashSet;
+}
+
+void Folder::onDirListFinished() {
+    DirListJob* job = static_cast<DirListJob*>(sender());
+    if(job->isCancelled()) { // this is a cancelled job, ignore!
+        if(job == dirlist_job) {
+            dirlist_job = nullptr;
+            Q_EMIT finishLoading(); // this was the last job until now
+        }
+        return;
+    }
+    dirInfo_ = job->dirInfo();
+
+    FileInfoList files_to_add;
+    std::vector<FileInfoPair> files_to_update;
+    const auto& infos = job->files();
+
+    // with "search://", there is no update for infos and all of them should be added
+    if(strcmp(dirPath_.uriScheme().get(), "search") == 0) {
+        files_to_add = infos;
+        for(auto& file: files_to_add) {
+            files_[file->path().baseName().get()] = file;
+        }
+    }
+    else {
+        auto info_it = infos.cbegin();
+        for(; info_it != infos.cend(); ++info_it) {
+            const auto& info = *info_it;
+            auto it = files_.find(info->path().baseName().get());
+            if(it != files_.end()) {
+                files_to_update.push_back(std::make_pair(it->second, info));
+            }
+            else {
+                files_to_add.push_back(info);
+            }
+            files_[info->path().baseName().get()] = 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
+    if(dirlist_job) {
+        dirlist_job->cancel();
+    }
+    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();
+
+        // ensure future changes will be processed
+        has_idle_update_handler = false;
+    }
+
+    /* remove all existing files */
+    if(!files_.empty()) {
+        // FIXME: this is not very efficient :(
+        auto tmp = files();
+        files_.clear();
+        Q_EMIT filesRemoved(tmp);
+    }
+
+    /* Tell the world that we're about to reload the folder.
+     * It might be a good idea for users of the folder to disconnect
+     * from the folder temporarily and reconnect to it again after
+     * the folder complete the loading. This might reduce some
+     * unnecessary signal handling and UI updates. */
+    Q_EMIT startLoading();
+
+    dirInfo_.reset(); // clear dir info
+
+    /* also re-create a new file monitor */
+    // mon = GFileMonitorPtr{fm_monitor_directory(dir_path.gfile().get(), &err), false};
+    // FIXME: should we make this cancellable?
+    dirMonitor_ = GFileMonitorPtr{
+            g_file_monitor_directory(dirPath_.gfile().get(), G_FILE_MONITOR_WATCH_MOUNTS, nullptr, &err),
+            false
+    };
+
+    if(dirMonitor_) {
+        g_signal_connect(dirMonitor_.get(), "changed", G_CALLBACK(_onFileChangeEvents), this);
+    }
+    else {
+        qDebug("file monitor cannot be created: %s", err->message);
+        g_error_free(err);
+    }
+
+    Q_EMIT contentChanged();
+
+    /* run a new dir listing job */
+    // FIXME:
+    // defer_content_test = fm_config->defer_content_test;
+    dirlist_job = new DirListJob(dirPath_, defer_content_test ? DirListJob::FAST : DirListJob::DETAILED,
+                                 hasCutFiles() ? cutFilesHashSet_ : nullptr);
+    dirlist_job->setAutoDelete(true);
+    connect(dirlist_job, &DirListJob::error, this, &Folder::error, Qt::BlockingQueuedConnection);
+    connect(dirlist_job, &DirListJob::finished, this, &Folder::onDirListFinished, Qt::BlockingQueuedConnection);
+
+#if 0
+    if(wants_incremental) {
+        g_signal_connect(dirlist_job, "files-found", G_CALLBACK(on_dirlist_job_files_found), folder);
+    }
+    fm_dir_list_job_set_incremental(dirlist_job, wants_incremental);
+#endif
+
+    dirlist_job->runAsync();
+
+    /* also reload filesystem info.
+     * FIXME: is this needed? */
+    queryFilesystemInfo();
+}
+
+#if 0
+
+/**
+ * Folder::is_incremental
+ * @folder: folder to test
+ *
+ * Checks if a folder is incrementally loaded.
+ * After an FmFolder object is obtained from calling Folder::from_path(),
+ * if it's not yet loaded, it begins loading the content of the folder
+ * and emits "start-loading" signal. Most of the time, the info of the
+ * files in the folder becomes available only after the folder is fully
+ * loaded. That means, after the "finish-loading" signal is emitted.
+ * Before the loading is finished, Folder::get_files() returns nothing.
+ * You can tell if a folder is still being loaded with Folder::is_loaded().
+ *
+ * However, for some special FmFolder types, such as the ones handling
+ * search:// URIs, we want to access the file infos while the folder is
+ * still being loaded (the search is still ongoing).
+ * The content of the folder grows incrementally and Folder::get_files()
+ * returns files currently being loaded even when the folder is not
+ * fully loaded. This is what we called incremental.
+ * Folder::is_incremental() tells you if the FmFolder has this feature.
+ *
+ * Returns: %true if @folder is incrementally loaded
+ *
+ * Since: 1.0.2
+ */
+bool Folder::is_incremental(FmFolder* folder) {
+    return wants_incremental;
+}
+
+#endif
+
+bool Folder::getFilesystemInfo(uint64_t* total_size, uint64_t* free_size) const {
+    if(has_fs_info) {
+        *total_size = fs_total_size;
+        *free_size = fs_free_size;
+        return true;
+    }
+    return false;
+}
+
+
+void Folder::onFileSystemInfoFinished() {
+    FileSystemInfoJob* job = static_cast<FileSystemInfoJob*>(sender());
+    if(job->isCancelled() || job != fsInfoJob_) { // this is a cancelled job, ignore!
+        fsInfoJob_ = nullptr;
+        has_fs_info = false;
+        return;
+    }
+    has_fs_info = job->isAvailable();
+    fs_total_size = job->size();
+    fs_free_size = job->freeSize();
+    filesystem_info_pending = true;
+    fsInfoJob_ = nullptr;
+    queueUpdate();
+}
+
+
+void Folder::queryFilesystemInfo() {
+    // G_LOCK(query);
+    if(fsInfoJob_)
+        return;
+    fsInfoJob_ = new FileSystemInfoJob{dirPath_};
+    fsInfoJob_->setAutoDelete(true);
+    connect(fsInfoJob_, &FileSystemInfoJob::finished, this, &Folder::onFileSystemInfoFinished, Qt::BlockingQueuedConnection);
+
+    fsInfoJob_->runAsync();
+    // G_UNLOCK(query);
+}
+
+
+#if 0
+/**
+ * Folder::block_updates
+ * @folder: folder to apply
+ *
+ * Blocks emitting signals for changes in folder, i.e. if some file was
+ * added, changed, or removed in folder after this API, no signal will be
+ * sent until next call to Folder::unblock_updates().
+ *
+ * Since: 1.2.0
+ */
+void Folder::block_updates(FmFolder* folder) {
+    /* g_debug("Folder::block_updates %p", folder); */
+    G_LOCK(lists);
+    /* just set the flag */
+    stop_emission = true;
+    G_UNLOCK(lists);
+}
+
+/**
+ * Folder::unblock_updates
+ * @folder: folder to apply
+ *
+ * Unblocks emitting signals for changes in folder. If some changes were
+ * in folder after previous call to Folder::block_updates() then these
+ * changes will be sent after this call.
+ *
+ * Since: 1.2.0
+ */
+void Folder::unblock_updates(FmFolder* folder) {
+    /* g_debug("Folder::unblock_updates %p", folder); */
+    G_LOCK(lists);
+    stop_emission = false;
+    /* query update now */
+    queue_update(folder);
+    G_UNLOCK(lists);
+    /* g_debug("Folder::unblock_updates OK"); */
+}
+
+/**
+ * Folder::make_directory
+ * @folder: folder to apply
+ * @name: display name for new directory
+ * @error: (allow-none) (out): location to save error
+ *
+ * Creates new directory in given @folder.
+ *
+ * Returns: %true in case of success.
+ *
+ * Since: 1.2.0
+ */
+bool Folder::make_directory(FmFolder* folder, const char* name, GError** error) {
+    GFile* dir, *gf;
+    FmPath* path;
+    bool ok;
+
+    dir = fm_path_to_gfile(dir_path);
+    gf = g_file_get_child_for_display_name(dir, name, error);
+    g_object_unref(dir);
+    if(gf == nullptr) {
+        return false;
+    }
+    ok = g_file_make_directory(gf, nullptr, error);
+    if(ok) {
+        path = fm_path_new_for_gfile(gf);
+        if(!_Folder::event_file_added(folder, path)) {
+            fm_path_unref(path);
+        }
+    }
+    g_object_unref(gf);
+    return ok;
+}
+
+void Folder::content_changed(FmFolder* folder) {
+    if(has_fs_info && !fs_info_not_avail) {
+        Folder::query_filesystem_info(folder);
+    }
+}
+
+#endif
+
+/* NOTE:
+ * GFileMonitor has some significant limitations:
+ * 1. Currently it can correctly emit unmounted event for a directory.
+ * 2. After a directory is unmounted, its content changes.
+ *    Inotify does not fire events for this so a forced reload is needed.
+ * 3. If a folder is empty, and later a filesystem is mounted to the
+ *    folder, its content should reflect the content of the newly mounted
+ *    filesystem. However, GFileMonitor and inotify do not emit events
+ *    for this case. A forced reload might be needed for this case as well.
+ * 4. Some limitations come from Linux/inotify. If FAM/gamin is used,
+ *    the condition may be different. More testing is needed.
+ */
+void Folder::onMountAdded(const Mount& mnt) {
+    /* If a filesystem is mounted over an existing folder,
+     * we need to refresh the content of the folder to reflect
+     * the changes. Besides, we need to create a new GFileMonitor
+     * for the newly-mounted filesystem as the inode already changed.
+     * GFileMonitor cannot detect this kind of changes caused by mounting.
+     * So let's do it ourselves. */
+    auto mountRoot = mnt.root();
+    if(mountRoot.isPrefixOf(dirPath_)) {
+        queueReload();
+    }
+    /* g_debug("FmFolder::mount_added"); */
+}
+
+void Folder::onMountRemoved(const Mount& mnt) {
+    /* g_debug("FmFolder::mount_removed"); */
+
+    /* NOTE: gvfs does not emit unmount signals for remote folders since
+     * GFileMonitor does not support remote filesystems at all.
+     * So here is the side effect, no unmount notifications.
+     * We need to generate the signal ourselves. */
+    if(!dirMonitor_) {
+        // this is only needed when we don't have a GFileMonitor
+        auto mountRoot = mnt.root();
+        if(mountRoot.isPrefixOf(dirPath_)) {
+            // if the current folder is under the unmounted path, generate the event ourselves
+            onDirChanged(G_FILE_MONITOR_EVENT_UNMOUNTED);
+        }
+    }
+}
+
+} // namespace Fm
diff --git a/src/core/folder.h b/src/core/folder.h
new file mode 100644 (file)
index 0000000..098d218
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2016 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __LIBFM2_QT_FM_FOLDER_H__
+#define __LIBFM2_QT_FM_FOLDER_H__
+
+#include <gio/gio.h>
+#include <cstdint>
+#include <memory>
+#include <vector>
+#include <unordered_map>
+#include <mutex>
+#include <functional>
+
+#include <QObject>
+#include <QtGlobal>
+#include "../libfmqtglobals.h"
+
+#include "gioptrs.h"
+#include "fileinfo.h"
+#include "job.h"
+#include "volumemanager.h"
+
+namespace Fm {
+
+class DirListJob;
+class FileSystemInfoJob;
+class FileInfoJob;
+
+
+class LIBFM_QT_API Folder: public QObject {
+    Q_OBJECT
+public:
+
+    explicit Folder();
+
+    explicit Folder(const FilePath& path);
+
+    virtual ~Folder();
+
+    static std::shared_ptr<Folder> fromPath(const FilePath& path);
+
+    bool makeDirectory(const char* name, GError** error);
+
+    void queryFilesystemInfo();
+
+    bool getFilesystemInfo(uint64_t* total_size, uint64_t* free_size) const;
+
+    void reload();
+
+    bool isIncremental() const;
+
+    bool isValid() const;
+
+    bool isLoaded() const;
+
+    std::shared_ptr<const FileInfo> fileByName(const char* name) const;
+
+    bool isEmpty() const;
+
+    FileInfoList files() const;
+
+    const FilePath& path() const;
+
+    const std::shared_ptr<const FileInfo> &info() const;
+
+    bool hadCutFilesUnset();
+
+    bool hasCutFiles();
+
+    void setCutFiles(const std::shared_ptr<const HashSet>& cutFilesHashSet);
+
+    void forEachFile(std::function<void (const std::shared_ptr<const FileInfo>&)> func) const {
+        std::lock_guard<std::mutex> lock{mutex_};
+        for(auto it = files_.begin(); it != files_.end(); ++it) {
+            func(it->second);
+        }
+    }
+
+Q_SIGNALS:
+    void startLoading();
+
+    void finishLoading();
+
+    void filesAdded(FileInfoList& addedFiles);
+
+    void filesChanged(std::vector<FileInfoPair>& changePairs);
+
+    void filesRemoved(FileInfoList& removedFiles);
+
+    void removed();
+
+    void changed();
+
+    void unmount();
+
+    void contentChanged();
+
+    void fileSystemChanged();
+
+    // FIXME: this API design is bad. We leave this here to be compatible with the old libfm C API.
+    // It might be better to remember the error state while loading the folder, and let the user of the
+    // API handle the error on finish.
+    void error(const GErrorPtr& err, Job::ErrorSeverity severity, Job::ErrorAction& response);
+
+private:
+
+    static void _onFileChangeEvents(GFileMonitor* monitor, GFile* file, GFile* other_file, GFileMonitorEvent event_type, Folder* _this) {
+        _this->onFileChangeEvents(monitor, file, other_file, event_type);
+    }
+    void onFileChangeEvents(GFileMonitor* monitor, GFile* file, GFile* other_file, GFileMonitorEvent event_type);
+    void onDirChanged(GFileMonitorEvent event_type);
+
+    void queueUpdate();
+    void queueReload();
+
+    bool eventFileAdded(const FilePath &path);
+    bool eventFileChanged(const FilePath &path);
+    void eventFileDeleted(const FilePath &path);
+
+private Q_SLOTS:
+
+    void processPendingChanges();
+
+    void onDirListFinished();
+
+    void onFileSystemInfoFinished();
+
+    void onFileInfoFinished();
+
+    void onIdleReload();
+
+    void onMountAdded(const Mount& mnt);
+
+    void onMountRemoved(const Mount& mnt);
+
+private:
+    FilePath dirPath_;
+    GFileMonitorPtr dirMonitor_;
+
+    std::shared_ptr<const FileInfo> dirInfo_;
+    DirListJob* dirlist_job;
+    std::vector<FileInfoJob*> fileinfoJobs_;
+    FileSystemInfoJob* fsInfoJob_;
+
+    std::shared_ptr<VolumeManager> volumeManager_;
+
+    /* for file monitor */
+    bool has_idle_reload_handler;
+    bool has_idle_update_handler;
+    FilePathList paths_to_add;
+    FilePathList paths_to_update;
+    FilePathList 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 */
+
+    // NOTE: Here, FileInfo::path().baseName().get() should be used as the key value, not FileInfo::name(),
+    // because the latter is not always the same as the former and the former will be used for comparison.
+    std::unordered_map<const std::string, std::shared_ptr<const FileInfo>, std::hash<std::string>> files_;
+
+    /* filesystem info - set in query thread, read in main */
+    uint64_t fs_total_size;
+    uint64_t fs_free_size;
+    GCancellablePtr fs_size_cancellable;
+
+    bool has_fs_info : 1;
+    bool defer_content_test : 1;
+
+    static std::unordered_map<FilePath, std::weak_ptr<Folder>, FilePathHash> cache_;
+    static QString cutFilesDirPath_;
+    static QString lastCutFilesDirPath_;
+    static std::shared_ptr<const HashSet> cutFilesHashSet_;
+    static std::mutex mutex_;
+};
+
+}
+
+#endif // __LIBFM_QT_FM2_FOLDER_H__
diff --git a/src/core/folderconfig.cpp b/src/core/folderconfig.cpp
new file mode 100644 (file)
index 0000000..1619c87
--- /dev/null
@@ -0,0 +1,275 @@
+/**
+ * SECTION:fm-folder-config
+ * @short_description: Folder specific settings cache.
+ * @title: FmFolderConfig
+ *
+ * @include: libfm/fm.h
+ *
+ * This API represents access to folder-specific configuration settings.
+ * Each setting is a key/value pair. To use it the descriptor should be
+ * opened first, then required operations performed, then closed. Each
+ * opened descriptor holds a lock on the cache so it is not adviced to
+ * keep it somewhere.
+ */
+
+#include "folderconfig.h"
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <errno.h>
+
+namespace Fm {
+
+CStrPtr FolderConfig::globalConfigFile_;
+
+// FIXME: sharing the same keyfile object everywhere is problematic
+// FIXME: this is MT-unsafe
+static GKeyFile* fc_cache = nullptr;
+static bool fc_cache_changed = FALSE;
+
+FolderConfig::FolderConfig():
+    keyFile_{nullptr},
+    changed_{false} {
+}
+
+FolderConfig::FolderConfig(const Fm::FilePath& path): FolderConfig{} {
+    (void)open(path);
+}
+
+FolderConfig::~FolderConfig() {
+    if(isOpened()) {
+        GErrorPtr err;
+        close(err);
+    }
+}
+
+bool FolderConfig::open(const Fm::FilePath& path) {
+    if(isOpened()) {  // the config is already opened
+        return false;
+    }
+
+    changed_ = FALSE;
+    if(path.isNative()) {
+        /* clear .directory file first */
+        auto sub_path = path.child(".directory");
+        configFilePath_ = sub_path.toString();
+
+        // FIXME: this only works for local filesystem and it's a blocking call. :-(
+        if(g_file_test(configFilePath_.get(), G_FILE_TEST_EXISTS)) {
+            keyFile_ = g_key_file_new();
+            if(g_key_file_load_from_file(keyFile_, configFilePath_.get(),
+                                         GKeyFileFlags(G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS),
+                                         nullptr) &&
+                    g_key_file_has_group(keyFile_, "File Manager")) {
+                group_ = CStrPtr{g_strdup("File Manager")};
+                return true;
+            }
+            g_key_file_free(keyFile_);
+        }
+    }
+
+    // No per-folder config file.
+    // use the global key file instead and use the folder path as group key
+    configFilePath_.reset();
+    group_ = path.toString();
+
+    // FIXME: we should use ref counting here. glib 2.36+ supports g_key_file_ref()
+    keyFile_ = fc_cache;
+    return true;
+}
+
+
+bool FolderConfig::close(GErrorPtr& err) {
+    bool ret = TRUE;
+    if(!isOpened()) {
+        return false;
+    }
+
+    if(configFilePath_) {
+        if(changed_) {
+            char* out;
+            gsize len;
+
+            out = g_key_file_to_data(keyFile_, &len, &err);
+            if(!out || !g_file_set_contents(configFilePath_.get(), out, len, &err)) {
+                ret = FALSE;
+            }
+            g_free(out);
+        }
+        configFilePath_.reset();
+        g_key_file_free(keyFile_);
+    }
+    else {
+        group_.reset();
+        if(changed_) {
+            fc_cache_changed = TRUE;
+        }
+    }
+    keyFile_ = nullptr;
+    return ret;
+}
+
+bool FolderConfig::isOpened() const {
+    return keyFile_ != nullptr;
+}
+
+bool FolderConfig::isEmpty() {
+    return !g_key_file_has_group(keyFile_, group_.get());
+}
+
+bool FolderConfig::getInteger(const char* key, int* val) {
+    GErrorPtr err;
+    auto ret = g_key_file_get_integer(keyFile_, group_.get(), key, &err);
+    if(err) {
+        return false;
+    }
+    *val = ret;
+    return true;
+}
+
+bool FolderConfig::getUint64(const char* key, uint64_t *val) {
+    GError* error = nullptr;
+#if GLIB_CHECK_VERSION(2, 26, 0)
+    guint64 ret = g_key_file_get_uint64(keyFile_, group_.get(), key, &error);
+#else
+    gchar* s, *end;
+    guint64 ret;
+
+    s = g_key_file_get_value(keyFile_, group_.get(), key, &error);
+#endif
+    if(error) {
+        g_error_free(error);
+        return FALSE;
+    }
+#if !GLIB_CHECK_VERSION(2, 26, 0)
+    ret = g_ascii_strtoull(s, &end, 10);
+    if(*s == '\0' || *end != '\0') {
+        g_free(s);
+        return FALSE;
+    }
+    g_free(s);
+#endif
+    *val = ret;
+    return TRUE;
+}
+
+bool FolderConfig::getDouble(const char* key,
+                                     double* val) {
+    GError* error = nullptr;
+    double ret = g_key_file_get_double(keyFile_, group_.get(), key, &error);
+    if(error) {
+        g_error_free(error);
+        return FALSE;
+    }
+    *val = ret;
+    return TRUE;
+}
+
+bool FolderConfig::getBoolean(const char* key, bool* val) {
+    GErrorPtr err;
+    auto ret = g_key_file_get_boolean(keyFile_, group_.get(), key, &err);
+    if(err) {
+        return false;
+    }
+    *val = ret;
+    return true;
+}
+
+char* FolderConfig::getString(const char* key) {
+    return g_key_file_get_string(keyFile_, group_.get(), key, nullptr);
+}
+
+char** FolderConfig::getStringList(const char* key, gsize* length) {
+    return g_key_file_get_string_list(keyFile_, group_.get(), key, length, nullptr);
+}
+
+
+void FolderConfig::setInteger(const char* key, int val) {
+    changed_ = TRUE;
+    g_key_file_set_integer(keyFile_, group_.get(), key, val);
+}
+
+void FolderConfig::setUint64(const char* key, uint64_t val) {
+    changed_ = TRUE;
+#if GLIB_CHECK_VERSION(2, 26, 0)
+    g_key_file_set_uint64(keyFile_, group_.get(), key, val);
+#else
+    gchar* result = g_strdup_printf("%" G_GUINT64_FORMAT, val);
+    g_key_file_set_value(keyFile_, group_.get(), key, result);
+    g_free(result);
+#endif
+}
+
+void FolderConfig::setDouble(const char* key, double val) {
+    changed_ = TRUE;
+    g_key_file_set_double(keyFile_, group_.get(), key, val);
+}
+
+void FolderConfig::setBoolean(const char* key, bool val) {
+    changed_ = TRUE;
+    g_key_file_set_boolean(keyFile_, group_.get(), key, val);
+}
+
+void FolderConfig::setString(const char* key, const char* string) {
+    changed_ = TRUE;
+    g_key_file_set_string(keyFile_, group_.get(), key, string);
+}
+
+void FolderConfig::setStringList(const char* key,
+                                      const gchar* const list[], gsize length) {
+    changed_ = TRUE;
+    g_key_file_set_string_list(keyFile_, group_.get(), key, list, length);
+}
+
+void FolderConfig::removeKey(const char* key) {
+    changed_ = TRUE;
+    g_key_file_remove_key(keyFile_, group_.get(), key, nullptr);
+}
+
+void FolderConfig::purge() {
+    changed_ = TRUE;
+    g_key_file_remove_group(keyFile_, group_.get(), nullptr);
+}
+
+// static
+void FolderConfig::saveCache(void) {
+    char* out;
+    gsize len;
+
+    /* if per-directory cache was changed since last invocation then save it */
+    if(fc_cache_changed && (out = g_key_file_to_data(fc_cache, &len, nullptr))) {
+        /* FIXME: create dir */
+        /* create temp file with settings */
+        GFilePtr gfile{g_file_new_for_path(globalConfigFile_.get()), false};
+        GErrorPtr err;
+        /* do safe replace now, the file is important enough to be lost */
+        if(g_file_replace_contents(gfile.get(), out, len, nullptr, true, G_FILE_CREATE_PRIVATE, nullptr, nullptr, &err)) {
+            fc_cache_changed = FALSE;
+        }
+        else {
+            g_warning("cannot save %s: %s", globalConfigFile_.get(), err->message);
+        }
+        g_free(out);
+    }
+}
+
+// static
+void FolderConfig::finalize(void) {
+    saveCache();
+    g_key_file_free(fc_cache);
+    fc_cache = nullptr;
+}
+
+// static
+void FolderConfig::init(const char* globalConfigFile) {
+    globalConfigFile_ = CStrPtr{g_strdup(globalConfigFile)};
+    fc_cache = g_key_file_new();
+    if(!g_key_file_load_from_file(fc_cache, globalConfigFile_.get(), G_KEY_FILE_NONE, nullptr)) {
+        // fail to load the config file.
+        // fallback to the legacy libfm config file for backward compatibility
+        CStrPtr legacyConfigFlie{g_build_filename(g_get_user_config_dir(), "libfm/dir-settings.conf", nullptr)};
+        g_key_file_load_from_file(fc_cache, legacyConfigFlie.get(), G_KEY_FILE_NONE, nullptr);
+    }
+}
+
+} // namespace Fm
diff --git a/src/core/folderconfig.h b/src/core/folderconfig.h
new file mode 100644 (file)
index 0000000..3959ae2
--- /dev/null
@@ -0,0 +1,80 @@
+#ifndef FOLDERCONFIG_H
+#define FOLDERCONFIG_H
+
+#include "../libfmqtglobals.h"
+
+#include "filepath.h"
+#include "gioptrs.h"
+
+#include <cstdint>
+
+namespace Fm {
+
+class LIBFM_QT_API FolderConfig {
+public:
+    FolderConfig();
+
+    explicit FolderConfig(const Fm::FilePath& path);
+
+    ~FolderConfig();
+
+    bool open(const Fm::FilePath& path);
+
+    bool close(GErrorPtr& err);
+
+    bool isOpened() const;
+
+    void purge(void);
+
+    void removeKey(const char* key);
+
+    void setStringList(const char* key, const gchar* const list[], gsize length);
+
+    void setString(const char* key, const char* string);
+
+    void setBoolean(const char* key, bool val);
+
+    void setDouble(const char* key, double val);
+
+    void setUint64(const char* key, std::uint64_t val);
+
+    void setInteger(const char* key, int val);
+
+    char** getStringList(const char* key, gsize* length);
+
+    char* getString(const char* key);
+
+    bool getBoolean(const char* key, bool* val);
+
+    bool getDouble(const char* key, double* val);
+
+    bool getUint64(const char* key, std::uint64_t* val);
+
+    bool getInteger(const char* key, int* val);
+
+    bool isEmpty(void);
+
+    // this needs to be called before using Fm::FolderConfig
+    static void init(const char* globalConfigFile);
+
+    static void finalize();
+
+    static void saveCache(void);
+
+// the object cannot be copied.
+private:
+    FolderConfig(const FolderConfig& other) = delete;
+    FolderConfig& operator=(const FolderConfig& other) = delete;
+
+private:
+    GKeyFile *keyFile_;
+    CStrPtr group_; /* allocated if not in cache */
+    CStrPtr configFilePath_; /* NULL if in cache */
+    bool changed_;
+
+    static CStrPtr globalConfigFile_;
+};
+
+} // namespace Fm
+
+#endif // FOLDERCONFIG_H
diff --git a/src/core/gioptrs.h b/src/core/gioptrs.h
new file mode 100644 (file)
index 0000000..ae22602
--- /dev/null
@@ -0,0 +1,141 @@
+#ifndef GIOPTRS_H
+#define GIOPTRS_H
+// define smart pointers for GIO data types
+
+#include <glib.h>
+#include <gio/gio.h>
+#include "gobjectptr.h"
+#include "cstrptr.h"
+
+namespace Fm {
+
+typedef GObjectPtr<GFile> GFilePtr;
+typedef GObjectPtr<GFileInfo> GFileInfoPtr;
+typedef GObjectPtr<GFileMonitor> GFileMonitorPtr;
+typedef GObjectPtr<GCancellable> GCancellablePtr;
+typedef GObjectPtr<GFileEnumerator> GFileEnumeratorPtr;
+
+typedef GObjectPtr<GInputStream> GInputStreamPtr;
+typedef GObjectPtr<GFileInputStream> GFileInputStreamPtr;
+typedef GObjectPtr<GOutputStream> GOutputStreamPtr;
+typedef GObjectPtr<GFileOutputStream> GFileOutputStreamPtr;
+
+typedef GObjectPtr<GIcon> GIconPtr;
+
+typedef GObjectPtr<GVolumeMonitor> GVolumeMonitorPtr;
+typedef GObjectPtr<GVolume> GVolumePtr;
+typedef GObjectPtr<GMount> GMountPtr;
+
+typedef GObjectPtr<GAppInfo> GAppInfoPtr;
+
+
+class GErrorPtr {
+public:
+    GErrorPtr(): err_{nullptr} {
+    }
+
+    GErrorPtr(GError*&& err) noexcept: err_{err} {
+        err = nullptr;
+    }
+
+    GErrorPtr(const GErrorPtr& other) = delete;
+
+    GErrorPtr(GErrorPtr&& other) noexcept: err_{other.err_} {
+        other.err_ = nullptr;
+    }
+
+    GErrorPtr(std::uint32_t domain, unsigned int code, const char* msg):
+        GErrorPtr{g_error_new_literal(domain, code, msg)} {
+    }
+
+    GErrorPtr(std::uint32_t domain, unsigned int code, const QString& msg):
+        GErrorPtr{domain, code, msg.toUtf8().constData()} {
+    }
+
+    ~GErrorPtr() {
+        reset();
+    }
+
+    std::uint32_t domain() const {
+        if(err_ != nullptr) {
+            return err_->domain;
+        }
+        return 0;
+    }
+
+    unsigned int code() const {
+        if(err_ != nullptr) {
+            return err_->code;
+        }
+        return 0;
+    }
+
+    QString message() const {
+        if(err_ != nullptr) {
+            return err_->message;
+        }
+        return QString();
+    }
+
+    void reset() {
+        if(err_) {
+            g_error_free(err_);
+        }
+        err_ = nullptr;
+    }
+
+    GError* get() const {
+        return err_;
+    }
+
+    GErrorPtr& operator = (const GErrorPtr& other) = delete;
+
+    GErrorPtr& operator = (GErrorPtr&& other) noexcept {
+        reset();
+        err_ = other.err_;
+        other.err_ = nullptr;
+        return *this;
+    }
+
+    GErrorPtr& operator = (GError*&& err) {
+        reset();
+        err_ = err;
+        err_ = nullptr;
+        return *this;
+    }
+
+    GError** operator&() {
+        return &err_;
+    }
+
+    GError* operator->() {
+        return err_;
+    }
+
+    const GError* operator->() const {
+        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
new file mode 100644 (file)
index 0000000..333bcc2
--- /dev/null
@@ -0,0 +1,104 @@
+#ifndef FM2_GOBJECTPTR_H
+#define FM2_GOBJECTPTR_H
+
+#include "../libfmqtglobals.h"
+#include <glib.h>
+#include <glib-object.h>
+#include <cstddef>
+#include <QDebug>
+
+namespace Fm {
+
+template <typename T>
+class LIBFM_QT_API GObjectPtr {
+public:
+
+    explicit GObjectPtr(): gobj_{nullptr} {
+    }
+
+    explicit GObjectPtr(T* gobj, bool add_ref = true): gobj_{gobj} {
+        if(gobj_ != nullptr && add_ref)
+            g_object_ref(gobj_);
+    }
+
+    GObjectPtr(const GObjectPtr& other): gobj_{other.gobj_ ? reinterpret_cast<T*>(g_object_ref(other.gobj_)) : nullptr} {
+    }
+
+    GObjectPtr(GObjectPtr&& other) noexcept: gobj_{other.release()} {
+    }
+
+    ~GObjectPtr() {
+        if(gobj_ != nullptr)
+            g_object_unref(gobj_);
+    }
+
+    T* get() const {
+        return gobj_;
+    }
+
+    T* release() {
+        T* tmp = gobj_;
+        gobj_ = nullptr;
+        return tmp;
+    }
+
+    void reset() {
+        if(gobj_ != nullptr)
+            g_object_unref(gobj_);
+        gobj_ = nullptr;
+    }
+
+    GObjectPtr& operator = (const GObjectPtr& other) {
+        if (*this == other)
+            return *this;
+
+        if(gobj_ != nullptr)
+            g_object_unref(gobj_);
+        gobj_ = other.gobj_ ? reinterpret_cast<T*>(g_object_ref(other.gobj_)) : nullptr;
+        return *this;
+    }
+
+    GObjectPtr& operator = (GObjectPtr&& other) noexcept {
+        if (this == &other)
+            return *this;
+
+        if(gobj_ != nullptr)
+            g_object_unref(gobj_);
+        gobj_ = other.release();
+        return *this;
+    }
+
+    GObjectPtr& operator = (T* gobj) {
+        if (*this == gobj)
+            return *this;
+
+        if(gobj_ != nullptr)
+            g_object_unref(gobj_);
+        gobj_ = gobj ? reinterpret_cast<T*>(g_object_ref(gobj_)) : nullptr;
+        return *this;
+    }
+
+    bool operator == (const GObjectPtr& other) const {
+        return gobj_ == other.gobj_;
+    }
+
+    bool operator == (T* gobj) const {
+        return gobj_ == gobj;
+    }
+
+    bool operator != (std::nullptr_t) const {
+        return gobj_ != nullptr;
+    }
+
+    operator bool() const {
+        return gobj_ != nullptr;
+    }
+
+private:
+    mutable T* gobj_;
+};
+
+
+} // namespace Fm
+
+#endif // FM2_GOBJECTPTR_H
diff --git a/src/core/iconinfo.cpp b/src/core/iconinfo.cpp
new file mode 100644 (file)
index 0000000..49c2eed
--- /dev/null
@@ -0,0 +1,176 @@
+#include "iconinfo.h"
+#include "iconinfo_p.h"
+#include <string.h>
+
+namespace Fm {
+
+std::unordered_map<GIcon*, std::shared_ptr<IconInfo>, IconInfo::GIconHash, IconInfo::GIconEqual> IconInfo::cache_;
+std::mutex IconInfo::mutex_;
+QList<QIcon> IconInfo::fallbackQicons_;
+
+static const char* fallbackIconNames[] = {
+    "unknown",
+    "application-octet-stream",
+    "application-x-generic",
+    "text-x-generic",
+    nullptr
+};
+
+static QIcon getFirst(const QList<QIcon> & icons)
+{
+    for (const auto & icon : icons) {
+        if (!icon.isNull())
+            return icon;
+    }
+    return QIcon{};
+}
+
+IconInfo::IconInfo(const char* name):
+    gicon_{g_themed_icon_new(name), false} {
+}
+
+IconInfo::IconInfo(const GIconPtr gicon):
+    gicon_{std::move(gicon)} {
+}
+
+IconInfo::~IconInfo() {
+}
+
+// static
+std::shared_ptr<const IconInfo> IconInfo::fromName(const char* name) {
+    GObjectPtr<GIcon> gicon{g_themed_icon_new(name), false};
+    return fromGIcon(gicon);
+}
+
+// static
+std::shared_ptr<const IconInfo> IconInfo::fromGIcon(GIconPtr gicon) {
+    if(Q_LIKELY(gicon)) {
+        std::lock_guard<std::mutex> lock{mutex_};
+        auto it = cache_.find(gicon.get());
+        if(it != cache_.end()) {
+            return it->second;
+        }
+        // not found in the cache, create a new entry for it.
+        auto icon = std::make_shared<IconInfo>(std::move(gicon));
+        cache_.insert(std::make_pair(icon->gicon_.get(), icon));
+        return icon;
+    }
+    return std::shared_ptr<const IconInfo>{};
+}
+
+void IconInfo::updateQIcons() {
+    std::lock_guard<std::mutex> lock{mutex_};
+    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<QIcon> IconInfo::qiconsFromNames(const char* const* names) {
+    QList<QIcon> 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<std::shared_ptr<const IconInfo>> IconInfo::emblems() const {
+    std::forward_list<std::shared_ptr<const IconInfo>> result;
+    if(hasEmblems()) {
+        const GList* emblems_glist = g_emblemed_icon_get_emblems(G_EMBLEMED_ICON(gicon_.get()));
+        for(auto l = emblems_glist; l; l = l->next) {
+            auto gemblem = G_EMBLEM(l->data);
+            GIconPtr gemblem_icon{g_emblem_get_icon(gemblem), true};
+            result.emplace_front(fromGIcon(gemblem_icon));
+        }
+        result.reverse();
+    }
+    return result;
+}
+
+QIcon IconInfo::internalQicon() const {
+    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;
+}
+
+// compatibility function for leagcy libfm
+// FIXME: deprecate this later.
+extern "C" GIcon* _fm_icon_from_name(const char* name) {
+    GIcon* gicon = nullptr;
+    if(G_LIKELY(name)) {
+        gchar *dot;
+        if(g_path_is_absolute(name)) {
+            GFile* gicon_file = g_file_new_for_path(name);
+            gicon = g_file_icon_new(gicon_file);
+            g_object_unref(gicon_file);
+        }
+        else if(G_UNLIKELY((dot = strrchr((char*)name, '.')) != NULL && dot > name &&
+                (g_ascii_strcasecmp(&dot[1], "png") == 0
+                 || g_ascii_strcasecmp(&dot[1], "svg") == 0
+                 || g_ascii_strcasecmp(&dot[1], "xpm") == 0))) {
+            /* some desktop entries have invalid icon name which contains
+               suffix so let strip the suffix from such invalid name */
+            dot = g_strndup(name, dot - name);
+            gicon = g_themed_icon_new_with_default_fallbacks(dot);
+            g_free(dot);
+        }
+        else {
+            gicon = g_themed_icon_new_with_default_fallbacks(name);
+        }
+    }
+    return gicon;
+}
+
+} // namespace Fm
diff --git a/src/core/iconinfo.h b/src/core/iconinfo.h
new file mode 100644 (file)
index 0000000..ad21f0c
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ *      fm-icon.h
+ *
+ *      Copyright 2009 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *      Copyright 2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ *      This file is a part of the Libfm library.
+ *
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2.1 of the License, or (at your option) any later version.
+ *
+ *      This library is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *      Lesser General Public License for more details.
+ *
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#ifndef __FM2_ICON_INFO_H__
+#define __FM2_ICON_INFO_H__
+
+#include "../libfmqtglobals.h"
+#include <gio/gio.h>
+#include "gioptrs.h"
+#include <memory>
+#include <mutex>
+#include <unordered_map>
+#include <forward_list>
+#include <QIcon>
+
+
+namespace Fm {
+
+class LIBFM_QT_API IconInfo: public std::enable_shared_from_this<IconInfo> {
+public:
+    friend class IconEngine;
+
+    explicit IconInfo() {}
+
+    explicit IconInfo(const char* name);
+
+    explicit IconInfo(const GIconPtr gicon);
+
+    ~IconInfo();
+
+    static std::shared_ptr<const IconInfo> fromName(const char* name);
+
+    static std::shared_ptr<const IconInfo> fromGIcon(GIconPtr gicon);
+
+    static std::shared_ptr<const IconInfo> fromGIcon(GIcon* gicon) {
+        return fromGIcon(GIconPtr{gicon, true});
+    }
+
+    static void updateQIcons();
+
+    GIconPtr gicon() const {
+        return gicon_;
+    }
+
+    QIcon qicon(const bool& transparent = false) const;
+
+    bool hasEmblems() const {
+        return G_IS_EMBLEMED_ICON(gicon_.get());
+    }
+
+    std::forward_list<std::shared_ptr<const IconInfo>> emblems() const;
+
+    bool isValid() const {
+        return gicon_ != nullptr;
+    }
+
+private:
+
+    static QList<QIcon> 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<QIcon> internalQicons_;
+
+    static std::unordered_map<GIcon*, std::shared_ptr<IconInfo>, GIconHash, GIconEqual> cache_;
+    static std::mutex mutex_;
+    static QList<QIcon> fallbackQicons_;
+};
+
+} // namespace Fm
+
+Q_DECLARE_METATYPE(std::shared_ptr<const Fm::IconInfo>)
+
+#endif /* __FM2_ICON_INFO_H__ */
diff --git a/src/core/iconinfo_p.h b/src/core/iconinfo_p.h
new file mode 100644 (file)
index 0000000..d2c3109
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2016  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_ICONENGINE_H
+#define FM_ICONENGINE_H
+
+#include <QIconEngine>
+#include <QPainter>
+#include "../libfmqtglobals.h"
+#include "iconinfo.h"
+#include <gio/gio.h>
+
+namespace Fm {
+
+class IconEngine: public QIconEngine {
+public:
+
+    IconEngine(std::shared_ptr<const Fm::IconInfo> info, const bool& transparent = false);
+
+    ~IconEngine();
+
+    virtual QSize actualSize(const QSize& size, QIcon::Mode mode, QIcon::State state) override;
+
+    // not supported
+    virtual void addFile(const QString& /*fileName*/, const QSize& /*size*/, QIcon::Mode /*mode*/, QIcon::State /*state*/) override {}
+
+    // not supported
+    virtual void addPixmap(const QPixmap& /*pixmap*/, QIcon::Mode /*mode*/, QIcon::State /*state*/) override {}
+
+    virtual QIconEngine* clone() const override;
+
+    virtual QString key() const override;
+
+    virtual void paint(QPainter* painter, const QRect& rect, QIcon::Mode mode, QIcon::State state) override;
+
+    virtual QPixmap pixmap(const QSize& size, QIcon::Mode mode, QIcon::State state) override;
+
+    virtual void virtual_hook(int id, void* data) override;
+
+private:
+    std::weak_ptr<const Fm::IconInfo> info_;
+    bool transparent_;
+};
+
+IconEngine::IconEngine(std::shared_ptr<const IconInfo> info, const bool& transparent):
+    info_{info}, transparent_{transparent} {
+}
+
+IconEngine::~IconEngine() {
+}
+
+QSize IconEngine::actualSize(const QSize& size, QIcon::Mode mode, QIcon::State state) {
+    auto info = info_.lock();
+    return info ? info->internalQicon().actualSize(size, mode, state) : QSize{};
+}
+
+QIconEngine* IconEngine::clone() const {
+    IconEngine* engine = new IconEngine(info_.lock());
+    return engine;
+}
+
+QString IconEngine::key() const {
+    return QStringLiteral("Fm::IconEngine");
+}
+
+void IconEngine::paint(QPainter* painter, const QRect& rect, QIcon::Mode mode, QIcon::State state) {
+    auto info = info_.lock();
+    if(info) {
+        if(transparent_) {
+            painter->save();
+            painter->setOpacity(0.45);
+        }
+        info->internalQicon().paint(painter, rect, Qt::AlignCenter, mode, state);
+        if(transparent_) {
+            painter->restore();
+        }
+    }
+}
+
+QPixmap IconEngine::pixmap(const QSize& size, QIcon::Mode mode, QIcon::State state) {
+    auto info = info_.lock();
+    return info ? info->internalQicon().pixmap(size, mode, state) : QPixmap{};
+}
+
+void IconEngine::virtual_hook(int id, void* data) {
+    auto info = info_.lock();
+    switch(id) {
+    case QIconEngine::AvailableSizesHook: {
+        auto* args = reinterpret_cast<QIconEngine::AvailableSizesArgument*>(data);
+        args->sizes = info ? info->internalQicon().availableSizes(args->mode, args->state) : QList<QSize>{};
+        break;
+    }
+    case QIconEngine::IconNameHook: {
+        QString* result = reinterpret_cast<QString*>(data);
+        *result = info ? info->internalQicon().name() : QString{};
+        break;
+    }
+#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
+    case QIconEngine::IsNullHook: {
+        bool* result = reinterpret_cast<bool*>(data);
+        *result = info ? info->internalQicon().isNull() : true;
+        break;
+    }
+#endif
+#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
+    case QIconEngine::ScaledPixmapHook: {
+        auto* arg = reinterpret_cast<QIconEngine::ScaledPixmapArgument*>(data);
+        arg->pixmap = info ? info->internalQicon().pixmap(arg->size, arg->mode, arg->state) : QPixmap{};
+        break;
+    }
+#endif
+    }
+}
+
+} // namespace Fm
+
+#endif // FM_ICONENGINE_H
diff --git a/src/core/job.cpp b/src/core/job.cpp
new file mode 100644 (file)
index 0000000..e597975
--- /dev/null
@@ -0,0 +1,57 @@
+#include "job.h"
+#include "job_p.h"
+
+namespace Fm {
+
+Job::Job():
+    paused_{false},
+    cancellable_{g_cancellable_new(), false},
+    cancellableHandler_{g_signal_connect(cancellable_.get(), "cancelled", G_CALLBACK(_onCancellableCancelled), this)} {
+}
+
+Job::~Job() {
+    if(cancellable_) {
+        g_cancellable_disconnect(cancellable_.get(), cancellableHandler_);
+    }
+}
+
+void Job::runAsync(QThread::Priority priority) {
+    auto thread = new JobThread(this);
+    connect(thread, &QThread::finished, thread, &QThread::deleteLater);
+    if(autoDelete()) {
+        connect(this, &Job::finished, this, &Job::deleteLater);
+    }
+    thread->start(priority);
+}
+
+void Job::cancel() {
+    g_cancellable_cancel(cancellable_.get());
+}
+
+void Job::run() {
+    exec();
+    Q_EMIT finished();
+}
+
+
+Job::ErrorAction Job::emitError(const GErrorPtr &err, Job::ErrorSeverity severity) {
+    ErrorAction response = ErrorAction::CONTINUE;
+    // if the error is already handled, don't emit it.
+    if(err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_FAILED_HANDLED) {
+        return response;
+    }
+    Q_EMIT error(err, severity, response);
+
+    if(severity == ErrorSeverity::CRITICAL || response == ErrorAction::ABORT) {
+        cancel();
+    }
+    else if(response == ErrorAction::RETRY ) {
+        /* If the job is already cancelled, retry is not allowed. */
+        if(isCancelled() || (err.domain() == G_IO_ERROR && err.code() == G_IO_ERROR_CANCELLED)) {
+            response = ErrorAction::CONTINUE;
+        }
+    }
+    return response;
+}
+
+} // namespace Fm
diff --git a/src/core/job.h b/src/core/job.h
new file mode 100644 (file)
index 0000000..944a436
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __LIBFM_QT_FM_JOB_H__
+#define __LIBFM_QT_FM_JOB_H__
+
+#include <QObject>
+#include <QtGlobal>
+#include <QThread>
+#include <QRunnable>
+#include <memory>
+#include <gio/gio.h>
+#include "gobjectptr.h"
+#include "gioptrs.h"
+#include "../libfmqtglobals.h"
+
+
+namespace Fm {
+
+/*
+ * Fm::Job can be used in several different modes.
+ * 1. run with QThreadPool::start()
+ * 2. call runAsync(), which will create a new QThread and move the object to the thread.
+ * 3. create a new QThread, and connect the started() signal to the slot Job::run()
+ * 4. Directly call Job::run(), which executes synchrounously as a normal blocking call
+*/
+
+class LIBFM_QT_API Job: public QObject, public QRunnable {
+    Q_OBJECT
+public:
+
+    enum class ErrorAction{
+        CONTINUE,
+        RETRY,
+        ABORT
+    };
+
+    enum class ErrorSeverity {
+        UNKNOWN,
+        WARNING,
+        MILD,
+        MODERATE,
+        SEVERE,
+        CRITICAL
+    };
+
+    explicit Job();
+
+    virtual ~Job();
+
+    bool isCancelled() const {
+        return g_cancellable_is_cancelled(cancellable_.get());
+    }
+
+    void runAsync(QThread::Priority priority = QThread::InheritPriority);
+
+    bool pause();
+
+    void resume();
+
+    const GCancellablePtr& cancellable() const {
+        return cancellable_;
+    }
+
+Q_SIGNALS:
+    void cancelled();
+
+    void finished();
+
+    // this signal should be connected with Qt::BlockingQueuedConnection
+    void error(const GErrorPtr& err, ErrorSeverity severity, ErrorAction& response);
+
+public Q_SLOTS:
+
+    void cancel();
+
+    void run() override;
+
+protected:
+    ErrorAction emitError(const GErrorPtr& err, ErrorSeverity severity = ErrorSeverity::MODERATE);
+
+    // all derived job subclasses should do their work in this method.
+    virtual void exec() = 0;
+
+private:
+    static void _onCancellableCancelled(GCancellable* cancellable, Job* _this) {
+        _this->onCancellableCancelled(cancellable);
+    }
+
+    void onCancellableCancelled(GCancellable* /*cancellable*/) {
+        Q_EMIT cancelled();
+    }
+
+private:
+    bool paused_;
+    GCancellablePtr cancellable_;
+    gulong cancellableHandler_;
+};
+
+
+}
+
+#endif // __LIBFM_QT_FM_JOB_H__
diff --git a/src/core/job_p.h b/src/core/job_p.h
new file mode 100644 (file)
index 0000000..d893d5f
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef JOB_P_H
+#define JOB_P_H
+
+#include <QThread>
+#include "job.h"
+
+namespace Fm {
+
+class JobThread: public QThread {
+    Q_OBJECT
+public:
+    JobThread(Job* job): job_{job} {
+    }
+
+protected:
+
+    void run() override {
+        job_->run();
+    }
+
+    Job* job_;
+};
+
+} // namespace Fm
+
+#endif // JOB_P_H
diff --git a/src/core/legacy/fm-app-info.c b/src/core/legacy/fm-app-info.c
new file mode 100644 (file)
index 0000000..921c6cb
--- /dev/null
@@ -0,0 +1,523 @@
+/*
+ *      fm-app-info.c
+ *
+ *      Copyright 2010 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *      Copyright 2012-2015 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ *      This file is a part of the Libfm library.
+ *
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2.1 of the License, or (at your option) any later version.
+ *
+ *      This library is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *      Lesser General Public License for more details.
+ *
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * SECTION:fm-app-info
+ * @short_description: FM application launch handlers
+ * @title: GAppInfo extensions
+ *
+ * @include: libfm/fm.h
+ *
+ */
+
+#include "fm-app-info.h"
+#include "fm-config.h"
+
+#include <string.h>
+#include <gio/gdesktopappinfo.h>
+
+static void append_file_to_cmd(GFile* gf, GString* cmd)
+{
+    char* file = g_file_get_path(gf);
+    char* quote;
+    if(file == NULL) /* trash:// gvfs is incomplete in resolving it */
+    {
+        /* we can retrieve real path from GVFS >= 1.13.3 */
+        if(g_file_has_uri_scheme(gf, "trash"))
+        {
+            GFileInfo *inf;
+            const char *orig_path;
+
+            inf = g_file_query_info(gf, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI,
+                                    G_FILE_QUERY_INFO_NONE, NULL, NULL);
+            if(inf == NULL) /* failed */
+                return;
+            orig_path = g_file_info_get_attribute_string(inf, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
+            if(orig_path != NULL) /* success */
+                file = g_filename_from_uri(orig_path, NULL, NULL);
+            g_object_unref(inf);
+        }
+        if(file == NULL)
+            return;
+    }
+    quote = g_shell_quote(file);
+    g_string_append(cmd, quote);
+    g_string_append_c(cmd, ' ');
+    g_free(quote);
+    g_free(file);
+}
+
+static void append_uri_to_cmd(GFile* gf, GString* cmd)
+{
+    char* uri = NULL;
+    if(!g_file_has_uri_scheme(gf, "file"))
+    {
+        /* When gvfs-fuse is in use, try to convert all URIs that are not file:// to 
+         * their corresponding FUSE-mounted local paths.  GDesktopAppInfo internally does this, too.
+         * With this, non-gtk+ applications can correctly open files in gvfs-mounted remote filesystems.
+         */
+        char* path = g_file_get_path(gf);
+        if(path)
+        {
+            uri = g_filename_to_uri(path, NULL, NULL);
+            g_free(path);
+        }
+    }
+    if(!uri)
+    {
+        uri = g_file_get_uri(gf);
+    }
+
+    if(uri)
+    {
+        char* quote = g_shell_quote(uri);
+        g_string_append(cmd, quote);
+        g_string_append_c(cmd, ' ');
+        g_free(quote);
+        g_free(uri);
+    }
+}
+
+static char* expand_exec_macros(GAppInfo* app, const char* full_desktop_path,
+                                GKeyFile* kf, GList** gfiles, GList **launching)
+{
+    GString* cmd;
+    const char* exec = g_app_info_get_commandline(app);
+    const char* p;
+    GFile *file = NULL;
+    GList *fl = NULL;
+
+    if (exec == NULL)
+        return NULL;
+    cmd = g_string_sized_new(1024);
+    for(p = exec; *p; ++p)
+    {
+        if(*p == '%')
+        {
+            ++p;
+            if(!*p)
+                break;
+            switch(*p)
+            {
+            case 'f':
+                if (file == NULL && *gfiles)
+                {
+                    *launching = *gfiles;
+                    file = G_FILE((*gfiles)->data);
+                    *gfiles = g_list_remove_link(*gfiles, *gfiles);
+                }
+                if (file)
+                    append_file_to_cmd(file, cmd);
+                break;
+            case 'F':
+                if (*gfiles)
+                    *launching = fl = *gfiles;
+                *gfiles = NULL;
+                g_list_foreach(fl, (GFunc)append_file_to_cmd, cmd);
+                break;
+            case 'u':
+                if (file == NULL && *gfiles)
+                {
+                    *launching = *gfiles;
+                    file = G_FILE((*gfiles)->data);
+                    *gfiles = g_list_remove_link(*gfiles, *gfiles);
+                }
+                if (file)
+                    append_uri_to_cmd(file, cmd);
+                break;
+            case 'U':
+                if (*gfiles)
+                    *launching = fl = *gfiles;
+                *gfiles = NULL;
+                g_list_foreach(fl, (GFunc)append_uri_to_cmd, cmd);
+                break;
+            case '%':
+                g_string_append_c(cmd, '%');
+                break;
+            case 'i':
+                if (kf == NULL)
+                    break;
+                {
+                    char* icon_name = g_key_file_get_locale_string(kf, "Desktop Entry",
+                                                                   "Icon", NULL, NULL);
+                    if(icon_name)
+                    {
+                        g_string_append(cmd, "--icon ");
+                        g_string_append(cmd, icon_name);
+                        g_free(icon_name);
+                    }
+                    break;
+                }
+            case 'c':
+                {
+                    const char* name = g_app_info_get_name(app);
+                    if(name)
+                    {
+                        char *quoted = g_shell_quote(name);
+                        g_string_append(cmd, quoted);
+                        g_free(quoted);
+                    }
+                    break;
+                }
+            case 'k':
+                /* append the file path of the desktop file */
+                if(full_desktop_path)
+                {
+                    /* FIXME: how about quoting here? */
+                    char* desktop_location = g_path_get_dirname(full_desktop_path);
+                    g_string_append(cmd, desktop_location);
+                    g_free(desktop_location);
+                }
+                break;
+            }
+        }
+        else
+            g_string_append_c(cmd, *p);
+    }
+
+    /* if files are provided but the Exec key doesn't contain %f, %F, %u, or %U */
+    if(*gfiles && !*launching)
+    {
+        /* treat as %f */
+        *launching = *gfiles;
+        file = G_FILE((*gfiles)->data);
+        *gfiles = g_list_remove_link(*gfiles, *gfiles);
+        g_string_append_c(cmd, ' ');
+        append_file_to_cmd(file, cmd);
+    }
+
+    return g_string_free(cmd, FALSE);
+}
+
+struct ChildSetup
+{
+    char* display;
+    char* sn_id;
+    pid_t pgid;
+};
+
+static void child_setup(gpointer user_data)
+{
+    struct ChildSetup* data = (struct ChildSetup*)user_data;
+    if(data->display)
+        g_setenv ("DISPLAY", data->display, TRUE);
+    if(data->sn_id)
+        g_setenv ("DESKTOP_STARTUP_ID", data->sn_id, TRUE);
+    /* Move child to grandparent group so it will not die with parent */
+    setpgid(0, data->pgid);
+}
+
+static void child_watch(GPid pid, gint status, gpointer user_data)
+{
+    /*
+     * Ensure that we don't double fork and break pkexec
+     */
+    g_spawn_close_pid(pid);
+}
+
+// defined in terminal.cpp
+char* expand_terminal(char* cmd, gboolean keep_open, GError** error);
+
+
+static gboolean do_launch(GAppInfo* appinfo, const char* full_desktop_path,
+                          GKeyFile* kf, GList** inp, GAppLaunchContext* ctx,
+                          GError** err)
+{
+    gboolean ret = FALSE;
+    GList *gfiles = NULL;
+    char* cmd, *path;
+    char** argv;
+    int argc;
+    gboolean use_terminal;
+    GAppInfoCreateFlags flags;
+    GPid pid;
+
+    cmd = expand_exec_macros(appinfo, full_desktop_path, kf, inp, &gfiles);
+    g_print("%s\n", cmd);
+    if (cmd == NULL || cmd[0] == '\0')
+    {
+        g_free(cmd);
+        /* FIXME: localize the string below in 1.3.0 */
+        g_set_error_literal(err, G_IO_ERROR, G_IO_ERROR_FAILED,
+                            "Desktop entry contains no valid Exec line");
+        return FALSE;
+    }
+    /* FIXME: do check for TryExec/Exec */
+    if(G_LIKELY(kf))
+        use_terminal = g_key_file_get_boolean(kf, "Desktop Entry", "Terminal", NULL);
+    else
+    {
+        flags = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(appinfo), "flags"));
+        use_terminal = (flags & G_APP_INFO_CREATE_NEEDS_TERMINAL) != 0;
+    }
+
+    if(use_terminal)
+    {
+        /* FIXME: is it right key to mark this option? */
+        gboolean keep_open = FALSE;
+        char* term_cmd;
+
+        if(G_LIKELY(kf))
+            keep_open = g_key_file_get_boolean(kf, "Desktop Entry",
+                                               "X-KeepTerminal", NULL);
+        term_cmd = expand_terminal(cmd, keep_open, err);
+        g_free(cmd);
+        if(!term_cmd)
+        {
+            g_list_free(gfiles);
+            return FALSE;
+        }
+        cmd = term_cmd;
+    }
+
+    g_debug("launch command: <%s>", cmd);
+    if(g_shell_parse_argv(cmd, &argc, &argv, err))
+    {
+        struct ChildSetup data;
+        if(ctx)
+        {
+            gboolean use_sn;
+            if(G_LIKELY(kf) && g_key_file_has_key(kf, "Desktop Entry", "StartupNotify", NULL))
+                use_sn = g_key_file_get_boolean(kf, "Desktop Entry", "StartupNotify", NULL);
+            else if(fm_config->force_startup_notify)
+            {
+                /* if the app doesn't explicitly ask us not to use sn,
+                 * and fm_config->force_startup_notify is TRUE, then
+                 * use it by default, unless it's a console app. */
+                use_sn = !use_terminal; /* we only use sn for GUI apps by default */
+                /* FIXME: console programs should use sn_id of terminal emulator instead. */
+            }
+            else
+                use_sn = FALSE;
+            data.display = g_app_launch_context_get_display(ctx, appinfo, gfiles);
+
+            if(use_sn)
+                data.sn_id = g_app_launch_context_get_startup_notify_id(ctx, appinfo, gfiles);
+            else
+                data.sn_id = NULL;
+        }
+        else
+        {
+            data.display = NULL;
+            data.sn_id = NULL;
+        }
+        g_debug("sn_id = %s", data.sn_id);
+
+        if(G_LIKELY(kf))
+            path = g_key_file_get_string(kf, "Desktop Entry", "Path", NULL);
+        else
+            path = NULL;
+
+        data.pgid = getpgid(getppid());
+                        /* only absolute path is usable, ignore others */
+        ret = g_spawn_async((path && path[0] == '/') ? path : NULL, argv, NULL,
+                            G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+                            child_setup, &data, &pid, err);
+        if (ret)
+            /* Ensure that we don't double fork and break pkexec */
+            g_child_watch_add(pid, child_watch, NULL);
+        else if (data.sn_id)
+            /* Notify launch context about failure */
+            g_app_launch_context_launch_failed(ctx, data.sn_id);
+
+        g_free(path);
+        g_free(data.display);
+        g_free(data.sn_id);
+
+        g_strfreev(argv);
+    }
+    g_free(cmd);
+    g_list_free(gfiles);
+    return ret;
+}
+
+/**
+ * fm_app_info_launch
+ * @appinfo: application info to launch
+ * @files: (element-type GFile): files to use in run substitutions
+ * @launch_context: (allow-none): a launch context
+ * @error: (out) (allow-none): location to store error
+ *
+ * Launches desktop application doing substitutions in application info.
+ *
+ * Returns: %TRUE if application was launched.
+ *
+ * Since: 0.1.15
+ */
+gboolean fm_app_info_launch(GAppInfo *appinfo, GList *files,
+                            GAppLaunchContext *launch_context, GError **error)
+{
+    gboolean supported = FALSE, ret = FALSE;
+    GList *launch_list = g_list_copy(files);
+    if(G_IS_DESKTOP_APP_INFO(appinfo))
+    {
+        const char *id;
+
+#if GLIB_CHECK_VERSION(2,24,0)
+        /* if GDesktopAppInfo knows the filename then let use it */
+        id = g_desktop_app_info_get_filename(G_DESKTOP_APP_INFO(appinfo));
+        if(id) /* this is a desktop entry file */
+        {
+            /* load the desktop entry file to obtain more info */
+            GKeyFile* kf = g_key_file_new();
+            supported = g_key_file_load_from_file(kf, id, 0, NULL);
+            if(supported) do {
+                ret = do_launch(appinfo, id, kf, &launch_list, launch_context, error);
+            } while (launch_list && ret);
+            g_key_file_free(kf);
+            id = NULL;
+        }
+        else /* otherwise try application id */
+#endif
+            id = g_app_info_get_id(appinfo);
+        if(id) /* this is an installed application */
+        {
+            /* load the desktop entry file to obtain more info */
+            GKeyFile* kf = g_key_file_new();
+            char* rel_path = g_strconcat("applications/", id, NULL);
+            char* full_desktop_path;
+            supported = g_key_file_load_from_data_dirs(kf, rel_path,
+                                                       &full_desktop_path, 0, NULL);
+            g_free(rel_path);
+            if(supported)
+            {
+                do {
+                    ret = do_launch(appinfo, full_desktop_path, kf, &launch_list,
+                                    launch_context, error);
+                } while (launch_list && ret);
+                g_free(full_desktop_path);
+            }
+            g_key_file_free(kf);
+        }
+        else
+        {
+#if GLIB_CHECK_VERSION(2,24,0)
+            if (!supported) /* it was launched otherwise, see above */
+#endif
+            {
+                /* If this is created with fm_app_info_create_from_commandline() */
+                if(g_object_get_data(G_OBJECT(appinfo), "flags"))
+                {
+                    supported = TRUE;
+                    do {
+                        ret = do_launch(appinfo, NULL, NULL, &launch_list,
+                                        launch_context, error);
+                    } while (launch_list && ret);
+                }
+            }
+        }
+    }
+    else
+        supported = FALSE;
+    g_list_free(launch_list);
+
+    if(!supported) /* fallback to GAppInfo::launch */
+        return g_app_info_launch(appinfo, files, launch_context, error);
+    return ret;
+}
+
+/**
+ * fm_app_info_launch_uris
+ * @appinfo: application info to launch
+ * @uris: (element-type char *): URIs to use in run substitutions
+ * @launch_context: (allow-none): a launch context
+ * @error: (out) (allow-none): location to store error
+ *
+ * Launches desktop application doing substitutions in application info.
+ *
+ * Returns: %TRUE if application was launched.
+ *
+ * Since: 0.1.15
+ */
+gboolean fm_app_info_launch_uris(GAppInfo *appinfo, GList *uris,
+                                 GAppLaunchContext *launch_context, GError **error)
+{
+    GList* gfiles = NULL;
+    gboolean ret;
+
+    for(;uris; uris = uris->next)
+    {
+        GFile* gf = g_file_new_for_uri((char*)uris->data);
+        if(gf)
+            gfiles = g_list_prepend(gfiles, gf);
+    }
+
+    gfiles = g_list_reverse(gfiles);
+    ret = fm_app_info_launch(appinfo, gfiles, launch_context, error);
+
+    g_list_foreach(gfiles, (GFunc)g_object_unref, NULL);
+    g_list_free(gfiles);
+    return ret;
+}
+
+/**
+ * fm_app_info_launch_default_for_uri
+ * @uri: the uri to show
+ * @launch_context: (allow-none): a launch context
+ * @error: (out) (allow-none): location to store error
+ *
+ * Utility function that launches the default application
+ * registered to handle the specified uri. Synchronous I/O
+ * is done on the uri to detect the type of the file if
+ * required.
+ *
+ * Returns: %TRUE if application was launched.
+ *
+ * Since: 0.1.15
+ */
+gboolean fm_app_info_launch_default_for_uri(const char *uri,
+                                            GAppLaunchContext *launch_context,
+                                            GError **error)
+{
+    /* FIXME: implement this */
+    return g_app_info_launch_default_for_uri(uri, launch_context, error);
+}
+
+/**
+ * fm_app_info_create_from_commandline
+ * @commandline: the commandline to use
+ * @application_name: (allow-none): the application name, or %NULL to use @commandline
+ * @flags: flags that can specify details of the created #GAppInfo
+ * @error: (out) (allow-none): location to store error
+ *
+ * Creates a new #GAppInfo from the given information.
+ *
+ * Note that for @commandline, the quoting rules of the Exec key of the
+ * <ulink url="http://freedesktop.org/Standards/desktop-entry-spec">freedesktop.org Desktop
+ * Entry Specification</ulink> are applied. For example, if the @commandline contains
+ * percent-encoded URIs, the percent-character must be doubled in order to prevent it from
+ * being swallowed by Exec key unquoting. See the specification for exact quoting rules.
+ *
+ * Returns: (transfer full): new #GAppInfo for given command.
+ *
+ * Since: 0.1.15
+ */
+GAppInfo* fm_app_info_create_from_commandline(const char *commandline,
+                                              const char *application_name,
+                                              GAppInfoCreateFlags flags,
+                                              GError **error)
+{
+    GAppInfo* app = g_app_info_create_from_commandline(commandline, application_name, flags, error);
+    g_object_set_data(G_OBJECT(app), "flags", GUINT_TO_POINTER(flags));
+    return app;
+}
diff --git a/src/core/legacy/fm-app-info.h b/src/core/legacy/fm-app-info.h
new file mode 100644 (file)
index 0000000..bae52c5
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *      fm-app-info.h
+ *
+ *      Copyright 2010 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ *      This file is a part of the Libfm library.
+ *
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2.1 of the License, or (at your option) any later version.
+ *
+ *      This library is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *      Lesser General Public License for more details.
+ *
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __FM_APP_INFO_H__
+#define __FM_APP_INFO_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+gboolean fm_app_info_launch(GAppInfo *appinfo, GList *files,
+                            GAppLaunchContext *launch_context, GError **error);
+
+gboolean fm_app_info_launch_uris(GAppInfo *appinfo, GList *uris,
+                                 GAppLaunchContext *launch_context, GError **error);
+
+gboolean fm_app_info_launch_default_for_uri(const char *uri,
+                                            GAppLaunchContext *launch_context,
+                                            GError **error);
+
+GAppInfo* fm_app_info_create_from_commandline(const char *commandline,
+                                              const char *application_name,
+                                              GAppInfoCreateFlags flags,
+                                              GError **error);
+
+G_END_DECLS
+
+#endif /* __FM_APP_INFO_H__ */
diff --git a/src/core/legacy/fm-config.c b/src/core/legacy/fm-config.c
new file mode 100644 (file)
index 0000000..6e0522a
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ *      fm-config.c
+ *
+ *      Copyright 2009 PCMan <pcman.tw@gmail.com>
+ *      Copyright 2009 Juergen Hoetzel <juergen@archlinux.org>
+ *      Copyright 2012-2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *      Copyright 2016 Mamoru TASAKA <mtasaka@fedoraproject.org>
+ *
+ *      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
+ */
+
+/**
+ * SECTION:fm-config
+ * @short_description: Configuration file support for applications that use libfm.
+ * @title: FmConfig
+ *
+ * @include: libfm/fm.h
+ *
+ * The #FmConfig represents basic configuration options that are used by
+ * libfm classes and methods. Methods of class #FmConfig allow use either
+ * default file (~/.config/libfm/libfm.conf) or another one to load the
+ * configuration and to save it.
+ */
+
+#include "fm-config.h"
+
+/* global config object */
+static FmConfig globalConfig_;
+FmConfig* fm_config = &globalConfig_;
+
+void fm_config_init() {
+    FmConfig *self = fm_config;
+    self->single_click = FM_CONFIG_DEFAULT_SINGLE_CLICK;
+    self->auto_selection_delay = FM_CONFIG_DEFAULT_AUTO_SELECTION_DELAY;
+    self->use_trash = FM_CONFIG_DEFAULT_USE_TRASH;
+    self->confirm_del = FM_CONFIG_DEFAULT_CONFIRM_DEL;
+    self->confirm_trash = FM_CONFIG_DEFAULT_CONFIRM_TRASH;
+    self->big_icon_size = FM_CONFIG_DEFAULT_BIG_ICON_SIZE;
+    self->small_icon_size = FM_CONFIG_DEFAULT_SMALL_ICON_SIZE;
+    self->pane_icon_size = FM_CONFIG_DEFAULT_PANE_ICON_SIZE;
+    self->thumbnail_size = FM_CONFIG_DEFAULT_THUMBNAIL_SIZE;
+    self->show_thumbnail = FM_CONFIG_DEFAULT_SHOW_THUMBNAIL;
+    self->thumbnail_local = FM_CONFIG_DEFAULT_THUMBNAIL_LOCAL;
+    self->thumbnail_max = FM_CONFIG_DEFAULT_THUMBNAIL_MAX;
+    /* show_internal_volumes defaulted to FALSE */
+    /* si_unit defaulted to FALSE */
+    /* terminal and archiver defaulted to NULL */
+    /* drop_default_action defaulted to 0 */
+    /* modules_blacklist and modules_whitelist defaulted to NULL */
+    /* format_cmd defaulted to NULL */
+    /* list_view_size_units defaulted to NULL */
+    /* saved_search defaulted to NULL */
+    self->advanced_mode = FALSE;
+    self->force_startup_notify = FM_CONFIG_DEFAULT_FORCE_S_NOTIFY;
+    self->backup_as_hidden = FM_CONFIG_DEFAULT_BACKUP_HIDDEN;
+    self->no_usb_trash = FM_CONFIG_DEFAULT_NO_USB_TRASH;
+    self->no_child_non_expandable = FM_CONFIG_DEFAULT_NO_EXPAND_EMPTY;
+    self->show_full_names = FM_CONFIG_DEFAULT_SHOW_FULL_NAMES;
+    self->shadow_hidden = FM_CONFIG_DEFAULT_SHADOW_HIDDEN;
+    self->only_user_templates = FM_CONFIG_DEFAULT_ONLY_USER_TEMPLATES;
+    self->template_run_app = FM_CONFIG_DEFAULT_TEMPLATE_RUN_APP;
+    self->template_type_once = FM_CONFIG_DEFAULT_TEMPL_TYPE_ONCE;
+    self->defer_content_test = FM_CONFIG_DEFAULT_DEFER_CONTENT_TEST;
+    self->quick_exec = FM_CONFIG_DEFAULT_QUICK_EXEC;
+    self->places_home = FM_CONFIG_DEFAULT_PLACES_HOME;
+    self->places_desktop = FM_CONFIG_DEFAULT_PLACES_DESKTOP;
+    self->places_root = FM_CONFIG_DEFAULT_PLACES_ROOT;
+    self->places_computer = FM_CONFIG_DEFAULT_PLACES_COMPUTER;
+    self->places_trash = FM_CONFIG_DEFAULT_PLACES_TRASH;
+    self->places_applications = FM_CONFIG_DEFAULT_PLACES_APPLICATIONS;
+    self->places_network = FM_CONFIG_DEFAULT_PLACES_NETWORK;
+    self->places_unmounted = FM_CONFIG_DEFAULT_PLACES_UNMOUNTED;
+    self->smart_desktop_autodrop = FM_CONFIG_DEFAULT_SMART_DESKTOP_AUTODROP;
+}
diff --git a/src/core/legacy/fm-config.h b/src/core/legacy/fm-config.h
new file mode 100644 (file)
index 0000000..ec834f6
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ *      fm-config.h
+ *
+ *      Copyright 2009 PCMan <pcman.tw@gmail.com>
+ *      Copyright 2009 Juergen Hoetzel <juergen@archlinux.org>
+ *      Copyright 2012-2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ *      This file is a part of the Libfm library.
+ *
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2.1 of the License, or (at your option) any later version.
+ *
+ *      This library is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *      Lesser General Public License for more details.
+ *
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+
+#ifndef __FM_CONFIG_H__
+#define __FM_CONFIG_H__
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+typedef struct _FmConfig            FmConfig;
+
+#define     FM_CONFIG_DEFAULT_SINGLE_CLICK      FALSE
+#define     FM_CONFIG_DEFAULT_USE_TRASH         TRUE
+#define     FM_CONFIG_DEFAULT_CONFIRM_DEL       TRUE
+#define     FM_CONFIG_DEFAULT_CONFIRM_TRASH     TRUE
+#define     FM_CONFIG_DEFAULT_NO_USB_TRASH      TRUE
+
+#define     FM_CONFIG_DEFAULT_BIG_ICON_SIZE     48
+#define     FM_CONFIG_DEFAULT_SMALL_ICON_SIZE   16
+#define     FM_CONFIG_DEFAULT_PANE_ICON_SIZE    16
+#define     FM_CONFIG_DEFAULT_THUMBNAIL_SIZE    128
+
+#define     FM_CONFIG_DEFAULT_SHOW_THUMBNAIL    TRUE
+#define     FM_CONFIG_DEFAULT_THUMBNAIL_LOCAL   TRUE
+#define     FM_CONFIG_DEFAULT_THUMBNAIL_MAX     2048
+
+#define     FM_CONFIG_DEFAULT_FORCE_S_NOTIFY    TRUE
+#define     FM_CONFIG_DEFAULT_BACKUP_HIDDEN     TRUE
+#define     FM_CONFIG_DEFAULT_NO_EXPAND_EMPTY   FALSE
+#define     FM_CONFIG_DEFAULT_SHOW_FULL_NAMES   FALSE
+#define     FM_CONFIG_DEFAULT_ONLY_USER_TEMPLATES FALSE
+#define     FM_CONFIG_DEFAULT_TEMPLATE_RUN_APP  FALSE
+#define     FM_CONFIG_DEFAULT_TEMPL_TYPE_ONCE   FALSE
+#define     FM_CONFIG_DEFAULT_SHADOW_HIDDEN     FALSE
+#define     FM_CONFIG_DEFAULT_DEFER_CONTENT_TEST FALSE
+#define     FM_CONFIG_DEFAULT_QUICK_EXEC        FALSE
+#define     FM_CONFIG_DEFAULT_SMART_DESKTOP_AUTODROP TRUE
+
+#define     FM_CONFIG_DEFAULT_PLACES_HOME       TRUE
+#define     FM_CONFIG_DEFAULT_PLACES_DESKTOP    TRUE
+#define     FM_CONFIG_DEFAULT_PLACES_ROOT       FALSE
+#define     FM_CONFIG_DEFAULT_PLACES_COMPUTER   FALSE
+#define     FM_CONFIG_DEFAULT_PLACES_TRASH      TRUE
+#define     FM_CONFIG_DEFAULT_PLACES_APPLICATIONS TRUE
+#define     FM_CONFIG_DEFAULT_PLACES_NETWORK    FALSE
+#define     FM_CONFIG_DEFAULT_PLACES_UNMOUNTED  TRUE
+
+#define     FM_CONFIG_DEFAULT_AUTO_SELECTION_DELAY 600
+
+
+/**
+ * FmConfig:
+ * @terminal: command line to launch terminal emulator
+ * @archiver: desktop_id of the archiver used
+ * @big_icon_size: size of big icons
+ * @small_icon_size: size of small icons
+ * @pane_icon_size: size of side pane icons
+ * @thumbnail_size: size of thumbnail icons
+ * @thumbnail_max: show thumbnails only for files not bigger than this, in KB or Kpix
+ * @auto_selection_delay: (since 1.2.0) delay for autoselection in single-click mode, in ms
+ * @drop_default_action: (since 1.2.0) default action on drop (see #FmDndDestDropAction)
+ * @single_click: single click to open file
+ * @use_trash: delete file to trash can
+ * @confirm_del: ask before deleting files
+ * @confirm_trash: (since 1.2.0) ask before moving files to trash can
+ * @show_thumbnail: show thumbnails
+ * @thumbnail_local: show thumbnails for local files only
+ * @show_internal_volumes: show system internal volumes in side pane. (udisks-only)
+ * @si_unit: use SI prefix for file sizes
+ * @advanced_mode: enable advanced features for experienced user
+ * @force_startup_notify: (since 1.0.1) use startup notify by default
+ * @backup_as_hidden: (since 1.0.1) treat backup files as hidden
+ * @no_usb_trash: (since 1.0.1) don't create trash folder on removable media
+ * @no_child_non_expandable: (since 1.0.1) hide expanders on empty folder
+ * @show_full_names: (since 1.2.0) always show full names in Icon View mode
+ * @shadow_hidden: (since 1.2.0) show icons of hidden files shadowed in the view
+ * @places_home: (since 1.2.0) show 'Home' item in Places
+ * @places_desktop: (since 1.2.0) show 'Desktop' item in Places
+ * @places_applications: (since 1.2.0) show 'Applications' item in Places
+ * @places_trash: (since 1.2.0) show 'Trash' item in Places
+ * @places_root: (since 1.2.0) show '/' item in Places
+ * @places_computer: (since 1.2.0) show 'My computer' item in Places
+ * @places_network: (since 1.2.0) show 'Network' item in Places
+ * @places_unmounted: (since 1.2.0) show unmounted internal volumes in Places
+ * @only_user_templates: (since 1.2.0) show only user defined templates in 'Create...' menu
+ * @template_run_app: (since 1.2.0) run default application after creation from template
+ * @template_type_once: (since 1.2.0) use only one template of each MIME type
+ * @defer_content_test: (since 1.2.0) defer test for content type on folder loading
+ * @quick_exec: (since 1.2.0) don't ask user for action on executable launch
+ * @modules_blacklist: (since 1.2.0) list of modules (mask in form "type:name") to never load
+ * @modules_whitelist: (since 1.2.0) list of excemptions from @modules_blacklist
+ * @list_view_size_units: (since 1.2.0) file size units in list view: h, k, M, G
+ * @format_cmd: (since 1.2.0) command to format the volume (device will be added)
+ * @smart_desktop_autodrop: (since 1.2.0) enable "smart shortcut" auto-action for ~/Desktop
+ * @saved_search: (since 1.2.0) internal saved data of fm_launch_search_simple()
+ */
+struct _FmConfig
+{
+    /*< private >*/
+    GObject parent;
+    char *_cfg_name;
+
+    /*< public >*/
+    char* terminal;
+    char* archiver;
+
+    gint big_icon_size;
+    gint small_icon_size;
+    gint pane_icon_size;
+    gint thumbnail_size;
+    gint thumbnail_max;
+    gint auto_selection_delay;
+    gint drop_default_action;
+
+    gboolean single_click;
+    gboolean use_trash;
+    gboolean confirm_del;
+    gboolean confirm_trash;
+    gboolean show_thumbnail;
+    gboolean thumbnail_local;
+    gboolean show_internal_volumes;
+    gboolean si_unit;
+    gboolean advanced_mode;
+    gboolean force_startup_notify;
+    gboolean backup_as_hidden;
+    gboolean no_usb_trash;
+    gboolean no_child_non_expandable;
+    gboolean show_full_names;
+    gboolean shadow_hidden;
+
+    gboolean places_home;
+    gboolean places_desktop;
+    gboolean places_applications;
+    gboolean places_trash;
+    gboolean places_root;
+    gboolean places_computer;
+    gboolean places_network;
+    gboolean places_unmounted;
+
+    gboolean only_user_templates;
+    gboolean template_run_app;
+    gboolean template_type_once;
+    gboolean defer_content_test;
+    gboolean quick_exec;
+
+    gchar **modules_blacklist;
+    gchar **modules_whitelist;
+    /*< private >*/
+    gchar **system_modules_blacklist; /* concatenated from system, don't save! */
+    /*< public >*/
+
+    gchar *list_view_size_units;
+    gchar *format_cmd;
+
+    gboolean smart_desktop_autodrop;
+    gchar *saved_search;
+    /*< private >*/
+    gpointer _reserved1; /* reserved space for updates until next ABI */
+    gpointer _reserved2;
+    gpointer _reserved3;
+    gpointer _reserved4;
+    gpointer _reserved5;
+    gpointer _reserved6;
+    gpointer _reserved7;
+    GFileMonitor *_cfg_mon;
+};
+
+/* global config object */
+G_MODULE_EXPORT extern FmConfig* fm_config;
+
+void fm_config_init();
+
+G_END_DECLS
+
+#endif /* __FM_CONFIG_H__ */
diff --git a/src/core/legacy/glib-compat.h b/src/core/legacy/glib-compat.h
new file mode 100644 (file)
index 0000000..61b17f7
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ *      glib-compat.h
+ *
+ *      Copyright 2011 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ *      This file is a part of the Libfm library.
+ *
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2.1 of the License, or (at your option) any later version.
+ *
+ *      This library is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *      Lesser General Public License for more details.
+ *
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __GLIB_COMPAT_H__
+#define __GLIB_COMPAT_H__
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* GLib prior 2.24 have no such macro */
+#ifndef G_DEFINE_INTERFACE
+#   define G_DEFINE_INTERFACE(TN, t_n, T_P) \
+static void     t_n##_default_init        (TN##Interface *klass); \
+GType t_n##_get_type (void) \
+{ \
+  static volatile gsize g_define_type_id__volatile = 0; \
+  if (g_once_init_enter (&g_define_type_id__volatile))  \
+    { \
+      GType g_define_type_id = \
+        g_type_register_static_simple (G_TYPE_INTERFACE, \
+                                       g_intern_static_string (#TN), \
+                                       sizeof (TN##Interface), \
+                                       (GClassInitFunc)t_n##_default_init, \
+                                       0, \
+                                       (GInstanceInitFunc)NULL, \
+                                       (GTypeFlags) 0); \
+      if (T_P) \
+        g_type_interface_add_prerequisite (g_define_type_id, T_P); \
+      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); \
+    } \
+  return g_define_type_id__volatile; \
+} /* closes t_n##_get_type() */
+#endif /* G_DEFINE_INTERFACE */
+
+#if !GLIB_CHECK_VERSION(2, 28, 0)
+
+/* This API was added in glib 2.28 */
+
+#define g_slist_free_full(slist, free_func)    \
+{ \
+g_slist_foreach(slist, (GFunc)free_func, NULL); \
+g_slist_free(slist); \
+}
+
+#define g_list_free_full(list, free_func)      \
+{ \
+g_list_foreach(list, (GFunc)free_func, NULL); \
+g_list_free(list); \
+}
+
+#endif
+
+#if !GLIB_CHECK_VERSION(2, 34, 0)
+/* This useful API was added in glib 2.34 */
+static inline GSList *g_slist_copy_deep(GSList *list, GCopyFunc func, gpointer user_data)
+{
+    GSList *new_list = g_slist_copy(list), *l;
+    for(l = new_list; l; l = l->next)
+        l->data = func(l->data, user_data);
+    return new_list;
+}
+#endif
+
+G_END_DECLS
+
+#endif
diff --git a/src/core/mimetype.cpp b/src/core/mimetype.cpp
new file mode 100644 (file)
index 0000000..19345bb
--- /dev/null
@@ -0,0 +1,64 @@
+#include "mimetype.h"
+#include <cstring>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+using namespace std;
+
+namespace Fm {
+
+std::unordered_map<const char*, std::shared_ptr<const MimeType>, CStrHash, CStrEqual> MimeType::cache_;
+std::mutex MimeType::mutex_;
+
+std::shared_ptr<const MimeType> MimeType::inodeDirectory_;  // inode/directory
+std::shared_ptr<const MimeType> MimeType::inodeShortcut_;  // inode/x-shortcut
+std::shared_ptr<const MimeType> MimeType::inodeMountPoint_;  // inode/mount-point
+std::shared_ptr<const MimeType> MimeType::desktopEntry_; // application/x-desktop
+
+
+MimeType::MimeType(const char* typeName):
+    name_{g_strdup(typeName)},
+    desc_{nullptr} {
+
+    GObjectPtr<GIcon> gicon{g_content_type_get_icon(typeName), false};
+    if(strcmp(typeName, "inode/directory") == 0)
+        g_themed_icon_prepend_name(G_THEMED_ICON(gicon.get()), "folder");
+    else if(g_content_type_can_be_executable(typeName))
+        g_themed_icon_append_name(G_THEMED_ICON(gicon.get()), "application-x-executable");
+
+    icon_ = IconInfo::fromGIcon(gicon);
+}
+
+MimeType::~MimeType () {
+}
+
+//static
+std::shared_ptr<const MimeType> MimeType::fromName(const char* typeName) {
+    std::shared_ptr<const MimeType> ret;
+    std::lock_guard<std::mutex> lock(mutex_);
+    auto it = cache_.find(typeName);
+    if(it == cache_.end()) {
+        ret = std::make_shared<MimeType>(typeName);
+        cache_.insert(std::make_pair(ret->name_.get(), ret));
+    }
+    else {
+        ret = it->second;
+    }
+    return ret;
+}
+
+// static
+std::shared_ptr<const MimeType> MimeType::guessFromFileName(const char* fileName) {
+    gboolean uncertain;
+    /* let skip scheme and host from non-native names */
+    auto uri_scheme = g_strstr_len(fileName, -1, "://");
+    if(uri_scheme)
+        fileName = strchr(uri_scheme + 3, '/');
+    if(fileName == nullptr)
+        fileName = "unknown";
+    auto type = CStrPtr{g_content_type_guess(fileName, nullptr, 0, &uncertain)};
+    return fromName(type.get());
+}
+
+} // namespace Fm
diff --git a/src/core/mimetype.h b/src/core/mimetype.h
new file mode 100644 (file)
index 0000000..887ce08
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ *      fm-mime-type.h
+ *
+ *      Copyright 2009 - 2012 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ *      This file is a part of the Libfm library.
+ *
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2.1 of the License, or (at your option) any later version.
+ *
+ *      This library is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *      Lesser General Public License for more details.
+ *
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _FM2_MIME_TYPE_H_
+#define _FM2_MIME_TYPE_H_
+
+#include "../libfmqtglobals.h"
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <mutex>
+#include <cstring>
+#include <forward_list>
+#include <functional>
+
+#include "cstrptr.h"
+#include "gobjectptr.h"
+#include "iconinfo.h"
+#include "thumbnailer.h"
+
+namespace Fm {
+
+class LIBFM_QT_API MimeType {
+public:
+    friend class Thumbnailer;
+
+    explicit MimeType(const char* typeName);
+
+    MimeType() = delete;
+
+    ~MimeType();
+
+    std::shared_ptr<const Thumbnailer> firstThumbnailer() const {
+        std::lock_guard<std::mutex> lock{mutex_};
+        return thumbnailers_.empty() ? nullptr : thumbnailers_.front();
+    }
+
+    void forEachThumbnailer(std::function<bool(const std::shared_ptr<const Thumbnailer>&)> func) const {
+        std::lock_guard<std::mutex> lock{mutex_};
+        for(auto& thumbnailer: thumbnailers_) {
+            if(func(thumbnailer)) {
+                break;
+            }
+        }
+    }
+
+    const std::shared_ptr<const IconInfo>& icon() const {
+        return icon_;
+    }
+
+    const char* name() const {
+        return name_.get();
+    }
+
+    const char* desc() const {
+        if(!desc_) {
+            desc_ = CStrPtr{g_content_type_get_description(name_.get())};
+        }
+        return desc_.get();
+    }
+
+    static std::shared_ptr<const MimeType> fromName(const char* typeName);
+
+    static std::shared_ptr<const MimeType> guessFromFileName(const char* fileName);
+
+    bool isUnknownType() const {
+        return g_content_type_is_unknown(name_.get());
+    }
+
+    bool isDesktopEntry() const {
+        return this == desktopEntry().get();
+    }
+
+    bool isText() const {
+        return g_content_type_is_a(name_.get(), "text/plain");
+    }
+
+    bool isImage() const {
+        return !std::strncmp("image/", name_.get(), 6);
+    }
+
+    bool isMountable() const {
+        return this == inodeMountPoint().get();
+    }
+
+    bool isShortcut() const {
+        return this == inodeShortcut().get();
+    }
+
+    bool isDir() const {
+        return this == inodeDirectory().get();
+    }
+
+    bool canBeExecutable() const {
+        return g_content_type_can_be_executable(name_.get());
+    }
+
+    static std::shared_ptr<const MimeType> inodeDirectory() {   // inode/directory
+        if(!inodeDirectory_)
+            inodeDirectory_ = fromName("inode/directory");
+        return inodeDirectory_;
+    }
+
+    static std::shared_ptr<const MimeType> inodeShortcut() {  // inode/x-shortcut
+        if(!inodeShortcut_)
+            inodeShortcut_ = fromName("inode/x-shortcut");
+        return inodeShortcut_;
+    }
+
+    static std::shared_ptr<const MimeType> inodeMountPoint() {  // inode/mount-point
+        if(!inodeMountPoint_)
+            inodeMountPoint_ = fromName("inode/mount-point");
+        return inodeMountPoint_;
+    }
+
+    static std::shared_ptr<const MimeType> desktopEntry() { // application/x-desktop
+        if(!desktopEntry_)
+            desktopEntry_ = fromName("application/x-desktop");
+        return desktopEntry_;
+    }
+
+private:
+    void removeThumbnailer(std::shared_ptr<const Thumbnailer>& thumbnailer) {
+        std::lock_guard<std::mutex> lock{mutex_};
+        thumbnailers_.remove(thumbnailer);
+    }
+
+    void addThumbnailer(std::shared_ptr<const Thumbnailer> thumbnailer) {
+        std::lock_guard<std::mutex> lock{mutex_};
+        thumbnailers_.push_front(std::move(thumbnailer));
+    }
+
+private:
+    std::shared_ptr<const IconInfo> icon_;
+    CStrPtr name_;
+    mutable CStrPtr desc_;
+    std::forward_list<std::shared_ptr<const Thumbnailer>> thumbnailers_;
+    static std::unordered_map<const char*, std::shared_ptr<const MimeType>, CStrHash, CStrEqual> cache_;
+    static std::mutex mutex_;
+
+    static std::shared_ptr<const MimeType> inodeDirectory_;  // inode/directory
+    static std::shared_ptr<const MimeType> inodeShortcut_;  // inode/x-shortcut
+    static std::shared_ptr<const MimeType> inodeMountPoint_;  // inode/mount-point
+    static std::shared_ptr<const MimeType> desktopEntry_; // application/x-desktop
+};
+
+
+} // namespace Fm
+
+#endif
diff --git a/src/core/templates.cpp b/src/core/templates.cpp
new file mode 100644 (file)
index 0000000..fbe358e
--- /dev/null
@@ -0,0 +1,136 @@
+#include "templates.h"
+#include "gioptrs.h"
+
+#include <algorithm>
+#include <QDebug>
+
+using namespace std;
+
+namespace Fm {
+
+std::weak_ptr<Templates> Templates::globalInstance_;
+
+TemplateItem::TemplateItem(std::shared_ptr<const FileInfo> file): fileInfo_{file} {
+}
+
+FilePath TemplateItem::filePath() const {
+    auto& target = fileInfo_->target();
+    if(fileInfo_->isDesktopEntry() && !target.empty()) {
+        if(target[0] == '/') { // target is an absolute path
+            return FilePath::fromLocalPath(target.c_str());
+        }
+        else { // resolve relative path
+            return fileInfo_->dirPath().relativePath(target.c_str());
+        }
+    }
+    return fileInfo_->path();
+}
+
+Templates::Templates() : QObject() {
+    auto* data_dirs = g_get_system_data_dirs();
+    // system-wide template dirs
+    for(auto data_dir = data_dirs; *data_dir; ++data_dir) {
+        CStrPtr dir_name{g_build_filename(*data_dir, "templates", nullptr)};
+        addTemplateDir(dir_name.get());
+    }
+
+    // user-specific template dir
+    CStrPtr dir_name{g_build_filename(g_get_user_data_dir(), "templates", nullptr)};
+    addTemplateDir(dir_name.get());
+
+    // $XDG_TEMPLATES_DIR (FIXME: this might change at runtime)
+    const gchar *special_dir = g_get_user_special_dir(G_USER_DIRECTORY_TEMPLATES);
+    if (special_dir) {
+        addTemplateDir(special_dir);
+    }
+}
+
+shared_ptr<Templates> Templates::globalInstance() {
+    auto templates = globalInstance_.lock();
+    if(!templates) {
+        templates = make_shared<Templates>();
+        globalInstance_ = templates;
+    }
+    return templates;
+}
+
+void Templates::addTemplateDir(const char* dirPathName) {
+    auto dir_path = FilePath::fromLocalPath(dirPathName);
+    if(dir_path.isValid()) {
+        auto folder = Folder::fromPath(dir_path);
+        if(folder->isLoaded()) {
+            const auto files = folder->files();
+            for(auto& file : files) {
+                items_.emplace_back(std::make_shared<TemplateItem>(file));
+            }
+        }
+        connect(folder.get(), &Folder::filesAdded, this, &Templates::onFilesAdded);
+        connect(folder.get(), &Folder::filesChanged, this, &Templates::onFilesChanged);
+        connect(folder.get(), &Folder::filesRemoved, this, &Templates::onFilesRemoved);
+        connect(folder.get(), &Folder::removed, this, &Templates::onTemplateDirRemoved);
+        templateFolders_.emplace_back(std::move(folder));
+    }
+}
+
+void Templates::onFilesAdded(FileInfoList& addedFiles) {
+    for(auto& file : addedFiles) {
+        // FIXME: we do not support subdirs right now (only XFCE supports this)
+        if(file->isHidden() || file->isDir()) {
+            continue;
+        }
+        items_.emplace_back(std::make_shared<TemplateItem>(file));
+        // emit a signal for the addition
+        Q_EMIT itemAdded(items_.back());
+    }
+}
+
+void Templates::onFilesChanged(std::vector<FileInfoPair>& changePairs) {
+    for(auto& change: changePairs) {
+        auto& old_file = change.first;
+        auto& new_file = change.second;
+        auto it = std::find_if(items_.begin(), items_.end(), [&](const std::shared_ptr<TemplateItem>& item) {
+            return item->fileInfo() == old_file;
+        });
+        if(it != items_.end()) {
+            // emit a signal for the change
+            auto old = *it;
+            *it = std::make_shared<TemplateItem>(new_file);
+            Q_EMIT itemChanged(old, *it);
+        }
+    }
+}
+
+void Templates::onFilesRemoved(FileInfoList& removedFiles) {
+    for(auto& file : removedFiles) {
+        auto filePath = file->path();
+        auto it = std::remove_if(items_.begin(), items_.end(), [&](const std::shared_ptr<TemplateItem>& item) {
+            return item->fileInfo() == file;
+        });
+        for(auto removed_it = it; it != items_.end(); ++it) {
+            // emit a signal for the removal
+            Q_EMIT itemRemoved(*removed_it);
+        }
+        items_.erase(it, items_.end());
+    }
+}
+
+void Templates::onTemplateDirRemoved() {
+    // the whole template dir is removed
+    auto folder = static_cast<Folder*>(sender());
+    if(!folder) {
+        return;
+    }
+    auto dirPath = folder->path();
+
+    // remote all files under this dir
+    auto it = std::remove_if(items_.begin(), items_.end(), [&](const std::shared_ptr<TemplateItem>& item) {
+        return dirPath.isPrefixOf(item->filePath());
+    });
+    for(auto removed_it = it; it != items_.end(); ++it) {
+        // emit a signal for the removal
+        Q_EMIT itemRemoved(*removed_it);
+    }
+    items_.erase(it, items_.end());
+}
+
+} // namespace Fm
diff --git a/src/core/templates.h b/src/core/templates.h
new file mode 100644 (file)
index 0000000..6f656dd
--- /dev/null
@@ -0,0 +1,100 @@
+#ifndef TEMPLATES_H
+#define TEMPLATES_H
+
+#include "../libfmqtglobals.h"
+
+#include <QObject>
+#include <memory>
+#include <vector>
+#include "folder.h"
+#include "fileinfo.h"
+#include "mimetype.h"
+#include "iconinfo.h"
+
+namespace Fm {
+
+class LIBFM_QT_API TemplateItem {
+public:
+    explicit TemplateItem(std::shared_ptr<const FileInfo> fileInfo);
+
+    QString displayName() const {
+        return fileInfo_->displayName();
+    }
+
+    const std::string& name() const {
+        return fileInfo_->name();
+    }
+
+    std::shared_ptr<const IconInfo> icon() const {
+        return fileInfo_->icon();
+    }
+
+    std::shared_ptr<const FileInfo> fileInfo() const {
+        return fileInfo_;
+    }
+
+    std::shared_ptr<const MimeType> mimeType() const {
+        return fileInfo_->mimeType();
+    }
+
+    FilePath filePath() const;
+
+private:
+    std::shared_ptr<const FileInfo> fileInfo_;
+};
+
+
+class LIBFM_QT_API Templates : public QObject {
+    Q_OBJECT
+public:
+    explicit Templates();
+
+    // FIXME: the first call to this method will get no templates since dir loading is in progress.
+    static std::shared_ptr<Templates> globalInstance();
+
+    void forEachItem(std::function<void (const std::shared_ptr<const TemplateItem>&)> func) const {
+        for(const auto& item : items_) {
+            func(item);
+        }
+    }
+
+    std::vector<std::shared_ptr<const TemplateItem>> items() const {
+        std::vector<std::shared_ptr<const TemplateItem>> tmp_items;
+        for(auto& item: items_) {
+            tmp_items.emplace_back(item);
+        }
+        return tmp_items;
+    }
+
+    bool hasTemplates() const {
+        return !items_.empty();
+    }
+
+Q_SIGNALS:
+    void itemAdded(const std::shared_ptr<const TemplateItem>& item);
+
+    void itemChanged(const std::shared_ptr<const TemplateItem>& oldItem, const std::shared_ptr<const TemplateItem>& newItem);
+
+    void itemRemoved(const std::shared_ptr<const TemplateItem>& item);
+
+private:
+    void addTemplateDir(const char* dirPathName);
+
+private Q_SLOTS:
+    void onFilesAdded(FileInfoList& addedFiles);
+
+    void onFilesChanged(std::vector<FileInfoPair>& changePairs);
+
+    void onFilesRemoved(FileInfoList& removedFiles);
+
+    void onTemplateDirRemoved();
+
+private:
+    std::vector<std::shared_ptr<TemplateItem>> items_;
+    std::vector<std::shared_ptr<Folder>> templateFolders_;
+    static std::weak_ptr<Templates> globalInstance_;
+};
+
+} // namespace Fm
+
+#endif // TEMPLATES_H
diff --git a/src/core/terminal.cpp b/src/core/terminal.cpp
new file mode 100644 (file)
index 0000000..807c7bd
--- /dev/null
@@ -0,0 +1,183 @@
+#include "terminal.h"
+
+namespace Fm {
+
+#include <glib.h>
+#include <gio/gdesktopappinfo.h>
+#include <string.h>
+#include <unistd.h>
+
+static std::string defaultTerminalName{"xterm"};
+
+#if !GLIB_CHECK_VERSION(2, 28, 0) && !HAVE_DECL_ENVIRON
+extern char** environ;
+#endif
+
+static void child_setup(gpointer user_data) {
+    /* Move child to grandparent group so it will not die with parent */
+    setpgid(0, (pid_t)(gsize)user_data);
+}
+
+bool launchTerminal(const char* programName, const FilePath& workingDir, Fm::GErrorPtr& error) {
+    /* read system terminals file */
+    GKeyFile* kf = g_key_file_new();
+    if(!g_key_file_load_from_file(kf, LIBFM_QT_DATA_DIR "/terminals.list", G_KEY_FILE_NONE, &error)) {
+        g_key_file_free(kf);
+        return false;
+    }
+    auto launch = g_key_file_get_string(kf, programName, "launch", nullptr);
+    auto desktop_id = g_key_file_get_string(kf, programName, "desktop_id", nullptr);
+
+    GDesktopAppInfo* appinfo = nullptr;
+    if(desktop_id) {
+        appinfo = g_desktop_app_info_new(desktop_id);
+    }
+
+    const gchar* cmd;
+    gchar* _cmd = nullptr;
+    if(appinfo) {
+        cmd = g_app_info_get_commandline(G_APP_INFO(appinfo));
+    }
+    else if(launch) {
+        cmd = _cmd = g_strdup_printf("%s %s", programName, launch);
+    }
+    else {
+        cmd = programName;
+    }
+
+#if 0 // FIXME: what's this?
+    if(custom_args) {
+        cmd = g_strdup_printf("%s %s", cmd, custom_args);
+        g_free(_cmd);
+        _cmd = (char*)cmd;
+    }
+#endif
+
+    char** argv;
+    int argc;
+    if(!g_shell_parse_argv(cmd, &argc, &argv, nullptr)) {
+        argv = nullptr;
+    }
+    g_free(_cmd);
+
+    if(appinfo) {
+        g_object_unref(appinfo);
+    }
+    if(!argv) { /* parsing failed */
+        return false;
+    }
+    char** envp;
+#if GLIB_CHECK_VERSION(2, 28, 0)
+    envp = g_get_environ();
+#else
+    envp = g_strdupv(environ);
+#endif
+
+    auto dir = workingDir ? workingDir.localPath() : nullptr;
+    if(dir) {
+#if GLIB_CHECK_VERSION(2, 32, 0)
+        envp = g_environ_setenv(envp, "PWD", dir.get(), TRUE);
+#else
+        char** env = envp;
+
+        if(env) while(*env != nullptr) {
+                if(strncmp(*env, "PWD=", 4) == 0) {
+                    break;
+                }
+                env++;
+            }
+        if(env == nullptr || *env == nullptr) {
+            gint length;
+
+            length = envp ? g_strv_length(envp) : 0;
+            envp = g_renew(gchar*, envp, length + 2);
+            env = &envp[length];
+            env[1] = nullptr;
+        }
+        else {
+            g_free(*env);
+        }
+        *env = g_strdup_printf("PWD=%s", dir);
+#endif
+    }
+
+    bool ret = g_spawn_async(dir.get(), argv, envp, G_SPAWN_SEARCH_PATH,
+                             child_setup, (gpointer)(gsize)getpgid(getppid()),
+                             nullptr, &error);
+    g_strfreev(argv);
+    g_strfreev(envp);
+    g_key_file_free(kf);
+    return ret;
+}
+
+std::vector<CStrPtr> allKnownTerminals() {
+    std::vector<CStrPtr> terminals;
+    GKeyFile* kf = g_key_file_new();
+    if(g_key_file_load_from_file(kf, LIBFM_QT_DATA_DIR "/terminals.list", G_KEY_FILE_NONE, nullptr)) {
+        gsize n;
+        auto programs = g_key_file_get_groups(kf, &n);
+        terminals.reserve(n);
+        for(auto name = programs; *name; ++name) {
+            terminals.emplace_back(*name);
+        }
+        g_free(programs);
+    }
+    g_key_file_free(kf);
+    return terminals;
+}
+
+// used by fm-app-info.c
+extern "C" char* expand_terminal(char* cmd, gboolean keep_open, GError** error) {
+    const char* program = nullptr;
+    CStrPtr open_arg;
+    CStrPtr noclose_arg;
+    CStrPtr custom_args;
+
+    /* 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)) {
+        if(g_key_file_has_group(kf, defaultTerminalName.c_str())) {
+            program = defaultTerminalName.c_str();
+            open_arg = CStrPtr{g_key_file_get_string(kf, program, "open_arg", nullptr)};
+            noclose_arg = CStrPtr{g_key_file_get_string(kf, program, "noclose_arg", nullptr)};
+            custom_args = CStrPtr{g_key_file_get_string(kf, program, "custom_args", nullptr)};
+        }
+    }
+    g_key_file_free(kf);
+
+    const char* opts;
+    /* if %s is not found, fallback to -e */
+    /* bug #3457335: Crash on application start with Terminal=true. */
+    /* fallback to xterm if a terminal emulator is not found. */
+    if(!program) {
+        /* FIXME: we should not hard code xterm here. :-(
+         * It's better to prompt the user and let he or she set
+         * his preferred terminal emulator. */
+        program = "xterm";
+        open_arg = CStrPtr{g_strdup("-e")};
+    }
+
+    if(keep_open && noclose_arg)
+        opts = noclose_arg.get();
+    else
+        opts = open_arg.get();
+
+    char* ret = nullptr;
+    if(custom_args)
+        ret = g_strdup_printf("%s %s %s %s", program, custom_args.get(),
+                              opts, cmd);
+    else
+        ret = g_strdup_printf("%s %s %s", program, opts, cmd);
+
+    return ret;
+}
+
+const std::string defaultTerminal() {
+    return defaultTerminalName;
+}
+
+void setDefaultTerminal(std::string program) {
+    defaultTerminalName = program;
+}
+
+} // namespace Fm
diff --git a/src/core/terminal.h b/src/core/terminal.h
new file mode 100644 (file)
index 0000000..db6f528
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef TERMINAL_H
+#define TERMINAL_H
+
+#include "../libfmqtglobals.h"
+#include "gioptrs.h"
+#include "filepath.h"
+#include <vector>
+#include <string>
+
+namespace Fm {
+
+LIBFM_QT_API bool launchTerminal(const char* programName, const FilePath& workingDir, GErrorPtr& error);
+
+LIBFM_QT_API std::vector<CStrPtr> allKnownTerminals();
+
+LIBFM_QT_API const std::string defaultTerminal();
+
+LIBFM_QT_API void setDefaultTerminal(std::string program);
+
+inline bool launchDefaultTerminal(const FilePath& workingDir, GErrorPtr& error) {
+    return launchTerminal(defaultTerminal().c_str(), workingDir, error);
+}
+
+} // namespace Fm
+
+#endif // TERMINAL_H
diff --git a/src/core/thumbnailer.cpp b/src/core/thumbnailer.cpp
new file mode 100644 (file)
index 0000000..ab2c80d
--- /dev/null
@@ -0,0 +1,141 @@
+#include "thumbnailer.h"
+#include "mimetype.h"
+#include <string>
+#include <QDebug>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+namespace Fm {
+
+std::mutex Thumbnailer::mutex_;
+std::vector<std::shared_ptr<Thumbnailer>> Thumbnailer::allThumbnailers_;
+
+Thumbnailer::Thumbnailer(const char* id, GKeyFile* kf):
+    id_{g_strdup(id)},
+    try_exec_{g_key_file_get_string(kf, "Thumbnailer Entry", "TryExec", nullptr)},
+    exec_{g_key_file_get_string(kf, "Thumbnailer Entry", "Exec", nullptr)} {
+}
+
+CStrPtr Thumbnailer::commandForUri(const char* uri, const char* output_file, guint size) const {
+    if(exec_) {
+        /* FIXME: how to handle TryExec? */
+
+        /* parse the command line and do required substitutions according to:
+         * 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<std::string, const char*>& hash, const char* data_dir) {
+    CStrPtr dir_path{g_build_filename(data_dir, "thumbnailers", nullptr)};
+    GDir* dir = g_dir_open(dir_path.get(), 0, nullptr);
+    if(dir) {
+        const char* basename;
+        while((basename = g_dir_read_name(dir)) != nullptr) {
+            /* we only want filenames with .thumbnailer extension */
+            if(G_LIKELY(g_str_has_suffix(basename, ".thumbnailer"))) {
+                hash.insert(std::make_pair(basename, data_dir));
+            }
+        }
+        g_dir_close(dir);
+    }
+}
+
+void Thumbnailer::loadAll() {
+    const gchar* const* data_dirs = g_get_system_data_dirs();
+    const gchar* const* data_dir;
+
+    /* use a temporary hash table to collect thumbnailer basenames
+     * key: basename of thumbnailer entry file
+     * value: data dir the thumbnailer entry file is in */
+    std::unordered_map<std::string, const char*> hash;
+
+    /* load user-specific thumbnailers */
+    find_thumbnailers_in_data_dir(hash, g_get_user_data_dir());
+
+    /* load system-wide thumbnailers */
+    for(data_dir = data_dirs; *data_dir; ++data_dir) {
+        find_thumbnailers_in_data_dir(hash, *data_dir);
+    }
+
+    /* load all found thumbnailers */
+    if(!hash.empty()) {
+        std::lock_guard<std::mutex> lock{mutex_};
+        GKeyFile* kf = g_key_file_new();
+        for(auto& item: hash) {
+            auto& base_name = item.first;
+            auto& dir_path = item.second;
+            CStrPtr file_path{g_build_filename(dir_path, "thumbnailers", base_name.c_str(), nullptr)};
+            if(g_key_file_load_from_file(kf, file_path.get(), G_KEY_FILE_NONE, nullptr)) {
+                auto thumbnailer = std::make_shared<Thumbnailer>(base_name.c_str(), kf);
+                if(thumbnailer->exec_) {
+                    char** mime_types = g_key_file_get_string_list(kf, "Thumbnailer Entry", "MimeType", nullptr, nullptr);
+                    if(mime_types) {
+                        for(char** name = mime_types; *name; ++name) {
+                            auto mime_type = MimeType::fromName(*name);
+                            if(mime_type) {
+                                std::const_pointer_cast<MimeType>(mime_type)->addThumbnailer(thumbnailer);
+                            }
+                        }
+                        g_strfreev(mime_types);
+                    }
+                }
+                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
new file mode 100644 (file)
index 0000000..91a21d7
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef FM2_THUMBNAILER_H
+#define FM2_THUMBNAILER_H
+
+#include "../libfmqtglobals.h"
+#include "cstrptr.h"
+#include <unordered_map>
+#include <vector>
+#include <memory>
+#include <mutex>
+
+namespace Fm {
+
+class MimeType;
+
+class LIBFM_QT_API Thumbnailer {
+public:
+    explicit Thumbnailer(const char *id, GKeyFile *kf);
+
+    CStrPtr commandForUri(const char* uri, const char* output_file, guint size) const;
+
+    bool run(const char* uri, const char* output_file, int size) const;
+
+    static void loadAll();
+
+private:
+    CStrPtr id_;
+    CStrPtr try_exec_; /* FIXME: is this useful? */
+    CStrPtr exec_;
+    //std::vector<std::shared_ptr<const MimeType>> mimeTypes_;
+
+    static std::mutex mutex_;
+    static std::vector<std::shared_ptr<Thumbnailer>> allThumbnailers_;
+};
+
+} // namespace Fm
+
+#endif // FM2_THUMBNAILER_H
diff --git a/src/core/thumbnailjob.cpp b/src/core/thumbnailjob.cpp
new file mode 100644 (file)
index 0000000..bddd7de
--- /dev/null
@@ -0,0 +1,293 @@
+#include "thumbnailjob.h"
+#include <string>
+#include <memory>
+#include <algorithm>
+#include <libexif/exif-loader.h>
+#include <QImageReader>
+#include <QDir>
+#include "thumbnailer.h"
+
+#include "core/legacy/fm-config.h"
+
+namespace Fm {
+
+QThreadPool* ThumbnailJob::threadPool_ = nullptr;
+
+bool ThumbnailJob::localFilesOnly_ = true;
+int ThumbnailJob::maxThumbnailFileSize_ = 0;
+
+ThumbnailJob::ThumbnailJob(FileInfoList files, int size):
+    files_{std::move(files)},
+    size_{size},
+    md5Calc_{g_checksum_new(G_CHECKSUM_MD5)} {
+}
+
+ThumbnailJob::~ThumbnailJob() {
+    g_checksum_free(md5Calc_);
+    // qDebug("delete  ThumbnailJob");
+}
+
+void ThumbnailJob::exec() {
+    for(auto& file: files_) {
+        if(isCancelled()) {
+            break;
+        }
+        auto image = loadForFile(file);
+        Q_EMIT thumbnailLoaded(file, size_, image);
+        results_.emplace_back(std::move(image));
+    }
+}
+
+QImage ThumbnailJob::readImageFromStream(GInputStream* stream, size_t len) {
+    // FIXME: should we set a limit here? Otherwise if len is too large, we can run out of memory.
+    std::unique_ptr<unsigned char[]> buffer{new unsigned char[len]}; // allocate enough buffer
+    unsigned char* pbuffer = buffer.get();
+    size_t totalReadSize = 0;
+    while(!isCancelled() && totalReadSize < len) {
+        size_t bytesToRead = totalReadSize + 4096 > len ? len - totalReadSize : 4096;
+        gssize readSize = g_input_stream_read(stream, pbuffer, bytesToRead, cancellable_.get(), nullptr);
+        if(readSize == 0) { // end of file
+            break;
+        }
+        else if(readSize == -1) { // error
+            return QImage();
+        }
+        totalReadSize += readSize;
+        pbuffer += readSize;
+    }
+    QImage image;
+    image.loadFromData(buffer.get(), totalReadSize);
+    return image;
+}
+
+QImage ThumbnailJob::loadForFile(const std::shared_ptr<const FileInfo> &file) {
+    if(!file->canThumbnail()) {
+        return QImage();
+    }
+
+    // thumbnails are stored in $XDG_CACHE_HOME/thumbnails/large|normal|failed
+    QString thumbnailDir{g_get_user_cache_dir()};
+    thumbnailDir += "/thumbnails/";
+
+    // don't make thumbnails for files inside the thumbnail directory
+    if(FilePath::fromLocalPath(thumbnailDir.toLocal8Bit().constData()).isParentOf(file->dirPath())) {
+        return QImage();
+    }
+
+    const char* subdir = size_ > 128 ? "large" : "normal";
+    thumbnailDir += subdir;
+
+    // generate base name of the thumbnail  => {md5 of uri}.png
+    auto origPath = file->path();
+    CStrPtr uri;
+    if(file->isSymlink()) {
+        // use the symlink target in the name to update the thumbnail
+        // if the file is changed to a symlink with the same time stamp
+        auto target = file->target();
+        if(!target.empty()) {
+            uri = FilePath::fromLocalPath(target.c_str()).uri();
+        }
+    }
+    if(!uri) {
+        uri = origPath.uri();
+    }
+
+    char thumbnailName[32 + 5];
+    // calculate md5 hash for the uri of the original file
+    g_checksum_update(md5Calc_, reinterpret_cast<const unsigned char*>(uri.get()), -1);
+    memcpy(thumbnailName, g_checksum_get_string(md5Calc_), 32);
+    memcpy(thumbnailName + 32, ".png", 5);
+    g_checksum_reset(md5Calc_); // reset the checksum calculator for next use
+
+    QString thumbnailFilename = thumbnailDir;
+    thumbnailFilename += '/';
+    thumbnailFilename += thumbnailName;
+    // qDebug() << "thumbnail:" << file->getName().c_str() << thumbnailFilename;
+
+    // try to load the thumbnail file if it exists
+    QImage thumbnail{thumbnailFilename};
+    if(thumbnail.isNull() || isThumbnailOutdated(file, thumbnail)) {
+        // the existing thumbnail cannot be loaded, generate a new one
+
+        // create the thumbnail dir as needd (FIXME: Qt file I/O is slow)
+        QDir().mkpath(thumbnailDir);
+
+        thumbnail = generateThumbnail(file, origPath, uri.get(), thumbnailFilename);
+    }
+    // resize to the size we need
+    if(thumbnail.width() > size_ || thumbnail.height() > size_) {
+        thumbnail = thumbnail.scaled(size_, size_, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+    }
+    return thumbnail;
+}
+
+bool ThumbnailJob::isSupportedImageType(const std::shared_ptr<const MimeType>& mimeType) const {
+    if(mimeType->isImage()) {
+        auto supportedTypes = QImageReader::supportedMimeTypes();
+        auto found = std::find(supportedTypes.cbegin(), supportedTypes.cend(), mimeType->name());
+        if(found != supportedTypes.cend())
+            return true;
+    }
+    return false;
+}
+
+bool ThumbnailJob::isThumbnailOutdated(const std::shared_ptr<const FileInfo>& file, const QImage &thumbnail) const {
+    QString thumb_mtime = thumbnail.text("Thumb::MTime");
+    return (thumb_mtime.isEmpty() || thumb_mtime.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<const FileInfo>& file, const FilePath& origPath, const char* uri, const QString& thumbnailFilename) {
+    QImage result;
+    auto mime_type = file->mimeType();
+    if(isSupportedImageType(mime_type)) {
+        GFileInputStreamPtr ins{g_file_read(origPath.gfile().get(), cancellable_.get(), nullptr), false};
+        if(!ins)
+            return QImage();
+        bool fromExif = false;
+        int rotate_degrees = 0;
+        if(strcmp(mime_type->name(), "image/jpeg") == 0) { // if this is a jpeg file
+            // try to get the thumbnail embedded in EXIF data
+            if(readJpegExif(G_INPUT_STREAM(ins.get()), result, rotate_degrees)) {
+                fromExif = true;
+            }
+        }
+        if(!fromExif) {  // not able to generate a thumbnail from the EXIF data
+            // load the original file and do the scaling ourselves
+            g_seekable_seek(G_SEEKABLE(ins.get()), 0, G_SEEK_SET, cancellable_.get(), nullptr);
+            result = readImageFromStream(G_INPUT_STREAM(ins.get()), file->size());
+        }
+        g_input_stream_close(G_INPUT_STREAM(ins.get()), nullptr, nullptr);
+
+        if(!result.isNull()) { // the image is successfully loaded
+            // scale the image as needed
+            int target_size = size_ > 128 ? 256 : 128;
+
+            // only scale the original image if it's too large
+            if(result.width() > target_size || result.height() > target_size) {
+                result = result.scaled(target_size, target_size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
+            }
+
+            if(rotate_degrees != 0) {
+                // degree values are 0, 90, 180, and 270 counterclockwise.
+                // In Qt, QMatrix does rotation counterclockwise as well.
+                // However, because the y axis of widget coordinate system is downward,
+                // the real effect of the coordinate transformation becomes clockwise rotation.
+                // So we need to use (360 - degree) here.
+                // Quote from QMatrix API doc:
+                // Note that if you apply a QMatrix to a point defined in widget
+                // coordinates, the direction of the rotation will be clockwise because
+                // the y-axis points downwards.
+                result = result.transformed(QMatrix().rotate(360 - rotate_degrees));
+            }
+
+            // save the generated thumbnail to disk (don't save png thumbnails for JPEG EXIF thumbnails since loading them is cheap)
+            if(!fromExif) {
+                result.setText("Thumb::MTime", QString::number(file->mtime()));
+                result.setText("Thumb::URI", uri);
+                result.save(thumbnailFilename, "PNG");
+            }
+            // qDebug() << "save thumbnail:" << thumbnailFilename;
+        }
+    }
+    else { // the image format is not supported, try to find an external thumbnailer
+        // try all available external thumbnailers for it until sucess
+        int target_size = size_ > 128 ? 256 : 128;
+        file->mimeType()->forEachThumbnailer([&](const std::shared_ptr<const Thumbnailer>& thumbnailer) {
+            if(thumbnailer->run(uri, thumbnailFilename.toLocal8Bit().constData(), target_size)) {
+                result = QImage(thumbnailFilename);
+            }
+            return !result.isNull(); // return true on success, and forEachThumbnailer() will stop.
+        });
+
+        if(!result.isNull()) {
+            // Some thumbnailers did not write the proper metadata required by the xdg spec to the output (such as evince-thumbnailer)
+            // Here we waste some time to fix them so next time we don't need to re-generate these thumbnails. :-(
+            bool changed = false;
+            if(Q_UNLIKELY(result.text("Thumb::MTime").isEmpty())) {
+                result.setText("Thumb::MTime", QString::number(file->mtime()));
+                changed = true;
+            }
+            if(Q_UNLIKELY(result.text("Thumb::URI").isEmpty())) {
+                result.setText("Thumb::URI", uri);
+                changed = true;
+            }
+            if(Q_UNLIKELY(changed)) {
+                // save the modified PNG file containing metadata to a file.
+                result.save(thumbnailFilename, "PNG");
+            }
+        }
+    }
+    return result;
+}
+
+QThreadPool* ThumbnailJob::threadPool() {
+    if(Q_UNLIKELY(threadPool_ == nullptr)) {
+        threadPool_ = new QThreadPool();
+        threadPool_->setMaxThreadCount(1);
+    }
+    return threadPool_;
+}
+
+void ThumbnailJob::setLocalFilesOnly(bool value) {
+    localFilesOnly_ = value;
+    if(fm_config) {
+        fm_config->thumbnail_local = localFilesOnly_;
+    }
+}
+
+void ThumbnailJob::setMaxThumbnailFileSize(int size) {
+    maxThumbnailFileSize_ = size;
+    if(fm_config) {
+        fm_config->thumbnail_max = maxThumbnailFileSize_;
+    }
+}
+
+
+} // namespace Fm
diff --git a/src/core/thumbnailjob.h b/src/core/thumbnailjob.h
new file mode 100644 (file)
index 0000000..632b14d
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef FM2_THUMBNAILJOB_H
+#define FM2_THUMBNAILJOB_H
+
+#include "../libfmqtglobals.h"
+#include "fileinfo.h"
+#include "gioptrs.h"
+#include "job.h"
+#include <QThreadPool>
+
+namespace Fm {
+
+class LIBFM_QT_API ThumbnailJob: public Job {
+    Q_OBJECT
+public:
+
+    explicit ThumbnailJob(FileInfoList files, int size);
+
+    ~ThumbnailJob();
+
+    int size() const {
+        return size_;
+    }
+
+    static QThreadPool* threadPool();
+
+    static void setLocalFilesOnly(bool value);
+
+    static bool localFilesOnly() {
+        return localFilesOnly_;
+    }
+
+    static int maxThumbnailFileSize() {
+        return maxThumbnailFileSize_;
+    }
+
+    static void setMaxThumbnailFileSize(int size);
+
+    const std::vector<QImage>& results() const {
+        return results_;
+    }
+
+Q_SIGNALS:
+    void thumbnailLoaded(const std::shared_ptr<const FileInfo>& file, int size, QImage thumbnail);
+
+protected:
+
+    void exec() override;
+
+private:
+
+    bool isSupportedImageType(const std::shared_ptr<const MimeType>& mimeType) const;
+
+    bool isThumbnailOutdated(const std::shared_ptr<const FileInfo>& file, const QImage& thumbnail) const;
+
+    QImage generateThumbnail(const std::shared_ptr<const FileInfo>& file, const FilePath& origPath, const char* uri, const QString& thumbnailFilename);
+
+    QImage readImageFromStream(GInputStream* stream, size_t len);
+
+    QImage loadForFile(const std::shared_ptr<const FileInfo>& file);
+
+    bool readJpegExif(GInputStream* stream, QImage& thumbnail, int& rotate_degrees);
+
+private:
+    FileInfoList files_;
+    int size_;
+    std::vector<QImage> results_;
+    GCancellablePtr cancellable_;
+    GChecksum* md5Calc_;
+
+    static QThreadPool* threadPool_;
+
+    static bool localFilesOnly_;
+    static int maxThumbnailFileSize_;
+};
+
+} // namespace Fm
+
+#endif // FM2_THUMBNAILJOB_H
diff --git a/src/core/totalsizejob.cpp b/src/core/totalsizejob.cpp
new file mode 100644 (file)
index 0000000..090be86
--- /dev/null
@@ -0,0 +1,141 @@
+#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
new file mode 100644 (file)
index 0000000..43c19c0
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef FM2_TOTALSIZEJOB_H
+#define FM2_TOTALSIZEJOB_H
+
+#include "../libfmqtglobals.h"
+#include "fileoperationjob.h"
+#include "filepath.h"
+#include <cstdint>
+#include "gioptrs.h"
+
+namespace Fm {
+
+class LIBFM_QT_API TotalSizeJob : public Fm::FileOperationJob {
+    Q_OBJECT
+public:
+    enum Flags {
+        DEFAULT = 0,
+        FOLLOW_LINKS = 1 << 0,
+        SAME_FS = 1 << 1,
+        PREPARE_MOVE = 1 << 2,
+        PREPARE_DELETE = 1 << 3
+    };
+
+    explicit TotalSizeJob(FilePathList paths = FilePathList{}, Flags flags = DEFAULT);
+
+    std::uint64_t totalSize() const {
+        return totalSize_;
+    }
+
+    std::uint64_t totalOnDiskSize() const {
+        return totalOndiskSize_;
+    }
+
+    unsigned int fileCount() const {
+        return fileCount_;
+    }
+
+protected:
+
+    void exec() override;
+
+private:
+    void exec(FilePath path, GFileInfoPtr inf);
+
+private:
+    FilePathList paths_;
+
+    int flags_;
+    std::uint64_t totalSize_;
+    std::uint64_t totalOndiskSize_;
+    unsigned int fileCount_;
+    const char* dest_fs_id;
+};
+
+} // namespace Fm
+
+#endif // FM2_TOTALSIZEJOB_H
diff --git a/src/core/trashjob.cpp b/src/core/trashjob.cpp
new file mode 100644 (file)
index 0000000..a79435d
--- /dev/null
@@ -0,0 +1,74 @@
+#include "trashjob.h"
+
+#include "core/legacy/fm-config.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);
+                     break;
+                }
+                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
new file mode 100644 (file)
index 0000000..65d93cf
--- /dev/null
@@ -0,0 +1,30 @@
+#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
new file mode 100644 (file)
index 0000000..7e0e0f6
--- /dev/null
@@ -0,0 +1,75 @@
+#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 {
+                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
new file mode 100644 (file)
index 0000000..22a5129
--- /dev/null
@@ -0,0 +1,22 @@
+#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
new file mode 100644 (file)
index 0000000..1c237f0
--- /dev/null
@@ -0,0 +1,47 @@
+#include "userinfocache.h"
+#include <pwd.h>
+#include <grp.h>
+
+namespace Fm {
+
+UserInfoCache* UserInfoCache::globalInstance_ = nullptr;
+std::mutex UserInfoCache::mutex_;
+
+UserInfoCache::UserInfoCache() : QObject() {
+}
+
+const std::shared_ptr<const UserInfo>& UserInfoCache::userFromId(uid_t uid) {
+    std::lock_guard<std::mutex> lock{mutex_};
+    auto it = users_.find(uid);
+    if(it != users_.end())
+        return it->second;
+    std::shared_ptr<const UserInfo> user;
+    auto pw = getpwuid(uid);
+    if(pw) {
+        user = std::make_shared<UserInfo>(uid, pw->pw_name, pw->pw_gecos);
+    }
+    return (users_[uid] = user);
+}
+
+const std::shared_ptr<const GroupInfo>& UserInfoCache::groupFromId(gid_t gid) {
+    std::lock_guard<std::mutex> lock{mutex_};
+    auto it = groups_.find(gid);
+    if(it != groups_.end())
+        return it->second;
+    std::shared_ptr<const GroupInfo> group;
+    auto gr = getgrgid(gid);
+    if(gr) {
+        group = std::make_shared<GroupInfo>(gid, gr->gr_name);
+    }
+    return (groups_[gid] = group);
+}
+
+// static
+UserInfoCache* UserInfoCache::globalInstance() {
+    std::lock_guard<std::mutex> lock{mutex_};
+    if(!globalInstance_)
+        globalInstance_ = new UserInfoCache();
+    return globalInstance_;
+}
+
+} // namespace Fm
diff --git a/src/core/userinfocache.h b/src/core/userinfocache.h
new file mode 100644 (file)
index 0000000..7338463
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef FM2_USERINFOCACHE_H
+#define FM2_USERINFOCACHE_H
+
+#include "../libfmqtglobals.h"
+#include <QObject>
+#include <string>
+#include <unordered_map>
+#include <sys/types.h>
+#include <memory>
+#include <mutex>
+
+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<const UserInfo>& userFromId(uid_t uid);
+
+    const std::shared_ptr<const GroupInfo>& groupFromId(gid_t gid);
+
+    static UserInfoCache* globalInstance();
+
+Q_SIGNALS:
+    void changed();
+
+private:
+    std::unordered_map<uid_t, std::shared_ptr<const UserInfo>> users_;
+    std::unordered_map<gid_t, std::shared_ptr<const GroupInfo>> groups_;
+    static UserInfoCache* globalInstance_;
+    static std::mutex mutex_;
+};
+
+} // namespace Fm
+
+#endif // FM2_USERINFOCACHE_H
diff --git a/src/core/vfs/fm-file.c b/src/core/vfs/fm-file.c
new file mode 100644 (file)
index 0000000..5f0f217
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ *      fm-file.c
+ *
+ *      Copyright 2012-2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ *      This file is a part of the Libfm library.
+ *
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2.1 of the License, or (at your option) any later version.
+ *
+ *      This library is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *      Lesser General Public License for more details.
+ *
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/**
+ * SECTION:fm-file
+ * @short_description: Extensions for GFile interface.
+ * @title: FmFile
+ *
+ * @include: libfm/fm.h
+ *
+ * The #FmFile represents interface to build extensions to GFile which
+ * will handle schemas that are absent in Glib/GVFS - such as "search:".
+ *
+ * To use it the GFile implementation should also implement FmFile vtable
+ * calls. The implementation should be added to list of known schemes via
+ * call to fm_file_add_vfs() then calls such as fm_file_new_for_uri() can
+ * use it.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "glib-compat.h"
+#define CHECK_MODULES() // this is not needed for libfm-qt
+
+#include "fm-file.h"
+
+#include <string.h>
+
+static GHashTable *schemes = NULL;
+
+#define FM_FILE_MODULE_MIN_VERSION 1
+#define FM_FILE_MODULE_MAX_VERSION 1
+
+G_LOCK_DEFINE_STATIC(vfs);
+
+G_DEFINE_INTERFACE(FmFile, fm_file, G_TYPE_FILE)
+
+static gboolean fm_file_wants_incremental_false(GFile *unused)
+{
+    return FALSE;
+}
+
+static void fm_file_default_init(FmFileInterface *iface)
+{
+    iface->wants_incremental = fm_file_wants_incremental_false;
+}
+
+
+static inline FmFileInitTable *fm_find_scheme(const char *name)
+{
+    FmFileInitTable *t;
+    CHECK_MODULES();
+    G_LOCK(vfs);
+    t = (FmFileInitTable*)g_hash_table_lookup(schemes, name);
+    G_UNLOCK(vfs);
+    return t;
+}
+
+/**
+ * fm_file_add_vfs
+ * @name: scheme to act upon
+ * @init: table of functions
+ *
+ * Adds VFS to list of extensions that will be applied on next call to
+ * fm_file_new_for_uri() or fm_file_new_for_commandline_arg(). The @name
+ * is a schema which will be handled by those calls.
+ *
+ * Since: 1.0.2
+ */
+void fm_file_add_vfs(const char *name, FmFileInitTable *init)
+{
+    G_LOCK(vfs);
+    if(g_hash_table_lookup(schemes, name) == NULL)
+        g_hash_table_insert(schemes, g_strdup(name), init);
+    G_UNLOCK(vfs);
+}
+
+/**
+ * fm_file_wants_incremental
+ * @file: file to inspect
+ *
+ * Checks if contents of directory @file cannot be retrieved at once so
+ * scanning it may be done in incremental manner for the best results.
+ *
+ * Returns: %TRUE if retrieve of contents of @file will be incremental.
+ *
+ * Since: 1.0.2
+ */
+gboolean fm_file_wants_incremental(GFile* file)
+{
+    FmFileInterface *iface;
+
+    g_return_val_if_fail(file != NULL, FALSE);
+    if(!FM_IS_FILE(file))
+        return FALSE;
+    iface = FM_FILE_GET_IFACE(file);
+    return iface->wants_incremental ? iface->wants_incremental(file) : FALSE;
+}
diff --git a/src/core/vfs/fm-file.h b/src/core/vfs/fm-file.h
new file mode 100644 (file)
index 0000000..37bf0d0
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ *      fm-file.h
+ *
+ *      Copyright 2012 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ *      This file is a part of the Libfm library.
+ *
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2.1 of the License, or (at your option) any later version.
+ *
+ *      This library is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *      Lesser General Public License for more details.
+ *
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _FM_FILE_H_
+#define _FM_FILE_H_ 1
+
+// #include "fm-module.h"
+
+#include <glib.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define FM_TYPE_FILE                    (fm_file_get_type())
+#define FM_FILE(obj)                    (G_TYPE_CHECK_INSTANCE_CAST((obj),\
+                                                FM_TYPE_FILE, FmFile))
+#define FM_IS_FILE(obj)                 (G_TYPE_CHECK_INSTANCE_TYPE((obj),\
+                                                FM_TYPE_FILE))
+#define FM_FILE_GET_IFACE(obj)          (G_TYPE_INSTANCE_GET_INTERFACE((obj),\
+                                                FM_TYPE_FILE, FmFileInterface))
+
+typedef struct _FmFile                  FmFile; /* Dummy typedef */
+typedef struct _FmFileInterface         FmFileInterface;
+
+/**
+ * FmFileInterface:
+ * @wants_incremental: VTable func, see fm_file_wants_incremental()
+ */
+struct _FmFileInterface
+{
+    /*< private >*/
+    GTypeInterface g_iface;
+
+    /*< public >*/
+    gboolean (*wants_incremental)(GFile* file);
+
+    /*< private >*/
+    gpointer _reserved1;
+    gpointer _reserved2;
+};
+
+typedef struct _FmFileInitTable         FmFileInitTable;
+
+/**
+ * FmFileInitTable:
+ * @new_for_uri: function to create new #GFile object from URI
+ *
+ * Functions to initialize FmFile instance.
+ *
+ * This structure is used for "vfs" module initialization. The key for
+ * module of this type is scheme name to support.
+ */
+struct _FmFileInitTable
+{
+    /*< public >*/
+    GFile * (*new_for_uri)(const char *uri);
+    /*< private >*/
+    gpointer _reserved1;
+    gpointer _reserved2;
+};
+
+GType           fm_file_get_type(void);
+
+void            fm_file_add_vfs(const char *name, FmFileInitTable *init);
+
+/* VTable calls */
+gboolean        fm_file_wants_incremental(GFile* file);
+
+/* Gfile functions replacements */
+GFile          *fm_file_new_for_uri(const char *uri);
+GFile          *fm_file_new_for_commandline_arg(const char *arg);
+
+void _fm_file_init(void);
+void _fm_file_finalize(void);
+
+/* for modules */
+#define FM_MODULE_vfs_VERSION 1
+// extern FmFileInitTable fm_module_init_vfs;
+
+G_END_DECLS
+#endif /* _FM_FILE_H_ */
diff --git a/src/core/vfs/fm-xml-file.c b/src/core/vfs/fm-xml-file.c
new file mode 100644 (file)
index 0000000..aab03c5
--- /dev/null
@@ -0,0 +1,1519 @@
+/*
+ *      fm-xml-file.c
+ *
+ *      Copyright 2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ *      This file is a part of libfm-extra package.
+ *
+ *      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
+ */
+
+/**
+ * SECTION:fm-xml-file
+ * @short_description: Simple XML parser.
+ * @title: FmXmlFile
+ *
+ * @include: libfm/fm-extra.h
+ *
+ * The FmXmlFile represents content of some XML file in form that can
+ * be altered and saved later.
+ *
+ * This parser has some simplifications on XML parsing:
+ * * Only UTF-8 encoding is supported
+ * * No user-defined entities, those should be converted externally
+ * * Processing instructions, comments and the doctype declaration are parsed but are not interpreted in any way
+ * The markup format does support:
+ * * Elements
+ * * Attributes
+ * * 5 standard entities: &amp;amp; &amp;lt; &amp;gt; &amp;quot; &amp;apos;
+ * * Character references
+ * * Sections marked as CDATA
+ *
+ * The application should respect g_type_init() if this parser is used
+ * without usage of libfm.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "fm-xml-file.h"
+
+#include <glib/gi18n-lib.h>
+#include "glib-compat.h"
+
+#include <stdlib.h>
+#include <errno.h>
+
+typedef struct
+{
+    gchar *name;
+    FmXmlFileHandler handler;
+    gboolean in_line : 1;
+} FmXmlFileTagDesc;
+
+struct _FmXmlFile
+{
+    GObject parent;
+    GList *items;
+    GString *data;
+    char *comment_pre;
+    FmXmlFileItem *current_item;
+    FmXmlFileTagDesc *tags; /* tags[0].name contains DTD */
+    guint n_tags; /* number of elements in tags */
+    guint line, pos;
+};
+
+struct _FmXmlFileClass
+{
+    GObjectClass parent_class;
+};
+
+struct _FmXmlFileItem
+{
+    FmXmlFileTag tag;
+    union {
+        gchar *tag_name; /* only for tag == FM_XML_FILE_TAG_NOT_HANDLED */
+        gchar *text; /* only for tag == FM_XML_FILE_TEXT, NULL if directive */
+    };
+    char **attribute_names;
+    char **attribute_values;
+    FmXmlFile *file;
+    FmXmlFileItem *parent;
+    GList **parent_list; /* points to file->items or to parent->children */
+    GList *children;
+    gchar *comment; /* a little trick: it is equal to text if it is CDATA */
+};
+
+
+G_DEFINE_TYPE(FmXmlFile, fm_xml_file, G_TYPE_OBJECT);
+
+static void fm_xml_file_finalize(GObject *object)
+{
+    FmXmlFile *self;
+    guint i;
+
+    g_return_if_fail(object != NULL);
+    g_return_if_fail(FM_IS_XML_FILE(object));
+
+    self = (FmXmlFile*)object;
+    self->current_item = NULL; /* we destroying it */
+    while (self->items)
+    {
+        g_assert(((FmXmlFileItem*)self->items->data)->file == self);
+        g_assert(((FmXmlFileItem*)self->items->data)->parent == NULL);
+        fm_xml_file_item_destroy(self->items->data);
+    }
+    for (i = 0; i < self->n_tags; i++)
+        g_free(self->tags[i].name);
+    g_free(self->tags);
+    if (self->data)
+        g_string_free(self->data, TRUE);
+    g_free(self->comment_pre);
+
+    G_OBJECT_CLASS(fm_xml_file_parent_class)->finalize(object);
+}
+
+static void fm_xml_file_class_init(FmXmlFileClass *klass)
+{
+    GObjectClass *g_object_class;
+
+    g_object_class = G_OBJECT_CLASS(klass);
+    g_object_class->finalize = fm_xml_file_finalize;
+}
+
+static void fm_xml_file_init(FmXmlFile *self)
+{
+    self->tags = g_new0(FmXmlFileTagDesc, 1);
+    self->n_tags = 1;
+    self->line = 1;
+}
+
+/**
+ * fm_xml_file_new
+ * @sibling: (allow-none): container to copy handlers data
+ *
+ * Creates new empty #FmXmlFile container. If @sibling is not %NULL
+ * then new container will have callbacks identical to set in @sibling.
+ * Use @sibling parameter if you need to work with few XML files that
+ * share the same schema or if you need to use the same tag ids for more
+ * than one file.
+ *
+ * Returns: (transfer full): newly created object.
+ *
+ * Since: 1.2.0
+ */
+FmXmlFile *fm_xml_file_new(FmXmlFile *sibling)
+{
+    FmXmlFile *self;
+    FmXmlFileTag i;
+
+    self = (FmXmlFile*)g_object_new(FM_XML_FILE_TYPE, NULL);
+    if (sibling && sibling->n_tags > 1)
+    {
+        self->n_tags = sibling->n_tags;
+        self->tags = g_renew(FmXmlFileTagDesc, self->tags, self->n_tags);
+        for (i = 1; i < self->n_tags; i++)
+        {
+            self->tags[i].name = g_strdup(sibling->tags[i].name);
+            self->tags[i].handler = sibling->tags[i].handler;
+        }
+    }
+    return self;
+}
+
+/**
+ * fm_xml_file_set_handler
+ * @file: the parser container
+ * @tag: tag to use @handler for
+ * @handler: callback for @tag
+ * @in_line: %TRUE if tag should not receive new line by fm_xml_file_to_data()
+ * @error: (allow-none) (out): location to save error
+ *
+ * Sets @handler for @file to be called on parse when @tag is found
+ * in XML data. This function will fail if some handler for @tag was
+ * aready set, in this case @error will be set appropriately.
+ *
+ * Returns: id for the @tag.
+ *
+ * Since: 1.2.0
+ */
+FmXmlFileTag fm_xml_file_set_handler(FmXmlFile *file, const char *tag,
+                                     FmXmlFileHandler handler, gboolean in_line,
+                                     GError **error)
+{
+    FmXmlFileTag i;
+
+    g_return_val_if_fail(file != NULL && FM_IS_XML_FILE(file), FM_XML_FILE_TAG_NOT_HANDLED);
+    g_return_val_if_fail(handler != NULL, FM_XML_FILE_TAG_NOT_HANDLED);
+    g_return_val_if_fail(tag != NULL, FM_XML_FILE_TAG_NOT_HANDLED);
+    for (i = 1; i < file->n_tags; i++)
+        if (strcmp(file->tags[i].name, tag) == 0)
+        {
+            g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                        _("Duplicate handler for tag <%s>"), tag);
+            return i;
+        }
+    file->tags = g_renew(FmXmlFileTagDesc, file->tags, i + 1);
+    file->tags[i].name = g_strdup(tag);
+    file->tags[i].handler = handler;
+    file->tags[i].in_line = in_line;
+    file->n_tags = i + 1;
+    /* g_debug("XML parser: added handler '%s' id %u", tag, (guint)i); */
+    return i;
+}
+
+
+/* parse */
+
+/*
+ * This function was taken from GLib sources and adapted to be used here.
+ * Copyright 2000, 2003 Red Hat, Inc.
+ *
+ * re-write the GString in-place, unescaping anything that escaped.
+ * most XML does not contain entities, or escaping.
+ */
+static gboolean
+unescape_gstring_inplace (//GMarkupParseContext  *context,
+                          GString              *string,
+                          //gboolean             *is_ascii,
+                          guint                *line_num,
+                          guint                *pos,
+                          gboolean              normalize_attribute,
+                          GError              **error)
+{
+  //char mask, *to;
+  char *to;
+  //int line_num = 1;
+  const char *from, *sol;
+
+  //*is_ascii = FALSE;
+
+  /*
+   * Meeks' theorum: unescaping can only shrink text.
+   * for &lt; etc. this is obvious, for &#xffff; more
+   * thought is required, but this is patently so.
+   */
+  //mask = 0;
+  for (from = to = string->str; *from != '\0'; from++, to++)
+    {
+      *to = *from;
+
+      //mask |= *to;
+      if (*to == '\n')
+      {
+        (*line_num)++;
+        *pos = 0;
+      }
+      if (normalize_attribute && (*to == '\t' || *to == '\n'))
+        *to = ' ';
+      if (*to == '\r')
+        {
+          *to = normalize_attribute ? ' ' : '\n';
+          if (from[1] == '\n')
+          {
+            from++;
+            (*line_num)++;
+            *pos = 0;
+          }
+        }
+      sol = from;
+      if (*from == '&')
+        {
+          from++;
+          if (*from == '#')
+            {
+              gboolean is_hex = FALSE;
+              gulong l;
+              gchar *end = NULL;
+
+              from++;
+
+              if (*from == 'x')
+                {
+                  is_hex = TRUE;
+                  from++;
+                }
+
+              /* digit is between start and p */
+              errno = 0;
+              if (is_hex)
+                l = strtoul (from, &end, 16);
+              else
+                l = strtoul (from, &end, 10);
+
+              if (end == from || errno != 0)
+                {
+                  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                              _("Failed to parse '%-.*s', which "
+                                "should have been a digit "
+                                "inside a character reference "
+                                "(&#234; for example) - perhaps "
+                                "the digit is too large"),
+                              (int)(end - from), from);
+                  return FALSE;
+                }
+              else if (*end != ';')
+                {
+                  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                              _("Character reference did not end with a "
+                                "semicolon; "
+                                "most likely you used an ampersand "
+                                "character without intending to start "
+                                "an entity - escape ampersand as &amp;"));
+                  return FALSE;
+                }
+              else
+                {
+                  /* characters XML 1.1 permits */
+                  if ((0 < l && l <= 0xD7FF) ||
+                      (0xE000 <= l && l <= 0xFFFD) ||
+                      (0x10000 <= l && l <= 0x10FFFF))
+                    {
+                      gchar buf[8];
+                      memset (buf, 0, 8);
+                      g_unichar_to_utf8 (l, buf);
+                      strncpy (to, buf, 8);
+                      to += strlen (buf) - 1;
+                      from = end;
+                      //if (l >= 0x80) /* not ascii */
+                        //mask |= 0x80;
+                    }
+                  else
+                    {
+                      g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                                  _("Character reference '%-.*s' does not "
+                                    "encode a permitted character"),
+                                  (int)(end - from), from);
+                      return FALSE;
+                    }
+                }
+            }
+
+          else if (strncmp (from, "lt;", 3) == 0)
+            {
+              *to = '<';
+              from += 2;
+            }
+          else if (strncmp (from, "gt;", 3) == 0)
+            {
+              *to = '>';
+              from += 2;
+            }
+          else if (strncmp (from, "amp;", 4) == 0)
+            {
+              *to = '&';
+              from += 3;
+            }
+          else if (strncmp (from, "quot;", 5) == 0)
+            {
+              *to = '"';
+              from += 4;
+            }
+          else if (strncmp (from, "apos;", 5) == 0)
+            {
+              *to = '\'';
+              from += 4;
+            }
+          else
+            {
+              if (*from == ';')
+                g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                            _("Empty entity '&;' seen; valid "
+                              "entities are: &amp; &quot; &lt; &gt; &apos;"));
+              else
+                {
+                  const char *end = strchr (from, ';');
+                  if (end)
+                    g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                                _("Entity name '%-.*s' is not known"),
+                                (int)(end-from), from);
+                  else
+                    g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                                _("Entity did not end with a semicolon; "
+                                  "most likely you used an ampersand "
+                                  "character without intending to start "
+                                  "an entity - escape ampersand as &amp;"));
+                }
+              return FALSE;
+            }
+        }
+      *pos += (from - sol) + 1;
+    }
+
+  /* g_debug("unescape_gstring_inplace completed"); */
+  g_assert (to - string->str <= (gint)string->len);
+  if (to - string->str != (gint)string->len)
+    g_string_truncate (string, to - string->str);
+
+  //*is_ascii = !(mask & 0x80);
+
+  return TRUE;
+}
+
+
+static inline void _update_file_ptr_part(FmXmlFile *file, const char *start,
+                                         const char *end)
+{
+    while (start < end)
+    {
+        if (*start == '\n')
+        {
+            file->line++;
+            file->pos = 0;
+        }
+        else
+            /* FIXME: advance by chars not bytes? */
+            file->pos++;
+        start++;
+    }
+}
+
+static inline void _update_file_ptr(FmXmlFile *file, int add_cols)
+{
+    guint i;
+    char *p;
+
+    for (i = file->data->len, p = file->data->str; i > 0; i--, p++)
+    {
+        if (*p == '\n')
+        {
+            file->line++;
+            file->pos = 0;
+        }
+        else
+            /* FIXME: advance by chars not bytes? */
+            file->pos++;
+    }
+    file->pos += add_cols;
+}
+
+static inline gboolean _is_space(char c)
+{
+    return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
+}
+
+/**
+ * fm_xml_file_parse_data
+ * @file: the parser container
+ * @text: data to parse
+ * @size: size of @text
+ * @error: (allow-none) (out): location to save error
+ * @user_data: data to pass to handlers
+ *
+ * Parses next chunk of @text data. Parsing stops at end of data or at any
+ * error. In latter case @error will be set appropriately.
+ *
+ * See also: fm_xml_file_finish_parse().
+ *
+ * Returns: %FALSE if parsing failed.
+ *
+ * Since: 1.2.0
+ */
+gboolean fm_xml_file_parse_data(FmXmlFile *file, const char *text,
+                                gsize size, GError **error, gpointer user_data)
+{
+    gsize ptr, len;
+    char *dst, *end, *tag, *name, *value;
+    GString *buff;
+    FmXmlFileItem *item;
+    gboolean closing, selfdo;
+    FmXmlFileTag i;
+    char **attrib_names, **attrib_values;
+    guint attribs;
+    char quote;
+
+    g_return_val_if_fail(file != NULL && FM_IS_XML_FILE(file), FALSE);
+_restart:
+    if (size == 0)
+        return TRUE;
+    /* if file->data has '<' as first char then we stopped at tag */
+    if (file->data && file->data->len && file->data->str[0] == '<')
+    {
+        for (ptr = 0; ptr < size; ptr++)
+            if (text[ptr] == '>')
+                break;
+        if (ptr == size) /* still no end of that tag */
+        {
+            g_string_append_len(file->data, text, size);
+            return TRUE;
+        }
+        /* we got a complete tag, nice, let parse it */
+        g_string_append_len(file->data, text, ptr);
+        ptr++;
+        text += ptr;
+        size -= ptr;
+        /* check for CDATA first */
+        if (file->data->len >= 11 /* <![CDATA[]] */ &&
+            strncmp(file->data->str, "<![CDATA[", 9) == 0)
+        {
+            end = file->data->str + file->data->len;
+            if (end[-2] != ']' || end[-1] != ']') /* find end of CDATA */
+            {
+                g_string_append_c(file->data, '>');
+                goto _restart;
+            }
+            if (file->current_item == NULL) /* CDATA at top level! */
+                g_warning("FmXmlFile: line %u: junk CDATA in XML file ignored",
+                          file->line);
+            else
+            {
+                item = fm_xml_file_item_new(FM_XML_FILE_TEXT);
+                item->text = item->comment = g_strndup(&file->data->str[9],
+                                                       file->data->len - 11);
+                fm_xml_file_item_append_child(file->current_item, item);
+            }
+            _update_file_ptr(file, 1);
+            g_string_truncate(file->data, 0);
+            goto _restart;
+        }
+        /* check for comment */
+        if (file->data->len >= 7 /* <!-- -- */ &&
+            strncmp(file->data->str, "<!--", 4) == 0)
+        {
+            end = file->data->str + file->data->len;
+            if (end[-2] != '-' || end[-1] != '-') /* find end of comment */
+            {
+                g_string_append_c(file->data, '>');
+                goto _restart;
+            }
+            g_free(file->comment_pre);
+            /* FIXME: not ignore duplicate comments */
+            if (_is_space(end[-3]))
+                file->comment_pre = g_strndup(&file->data->str[5],
+                                              file->data->len - 8);
+            else /* FIXME: check: XML spec says it should be not '-' */
+                file->comment_pre = g_strndup(&file->data->str[5],
+                                              file->data->len - 7);
+            _update_file_ptr(file, 1);
+            g_string_truncate(file->data, 0);
+            goto _restart;
+        }
+        /* check for DTD - it may be only at top level */
+        if (file->current_item == NULL && file->data->len >= 10 &&
+            strncmp(file->data->str, "<!DOCTYPE", 9) == 0 &&
+            _is_space(file->data->str[9]))
+        {
+            /* FIXME: can DTD contain any tags? count '<' and '>' pairs */
+            if (file->tags[0].name) /* duplicate DTD! */
+                g_warning("FmXmlFile: line %u: duplicate DTD, ignored",
+                          file->line);
+            else
+                file->tags[0].name = g_strndup(&file->data->str[10],
+                                               file->data->len - 10);
+            _update_file_ptr(file, 1);
+            g_string_truncate(file->data, 0);
+            goto _restart;
+        }
+        /* support directives such as <?xml ..... ?> */
+        if (file->data->len >= 4 /* <?x? */ &&
+            file->data->str[1] == '?' &&
+            file->data->str[file->data->len-1] == '?')
+        {
+            item = fm_xml_file_item_new(FM_XML_FILE_TEXT);
+            item->comment = g_strndup(&file->data->str[2], file->data->len - 3);
+            if (file->current_item != NULL)
+                fm_xml_file_item_append_child(file->current_item, item);
+            else
+            {
+                item->file = file;
+                item->parent_list = &file->items;
+                file->items = g_list_append(file->items, item);
+            }
+            _update_file_ptr(file, 1);
+            g_string_truncate(file->data, 0);
+            goto _restart;
+        }
+        closing = (file->data->str[1] == '/');
+        end = file->data->str + file->data->len;
+        selfdo = (!closing && end[-1] == '/');
+        if (selfdo)
+            end--;
+        tag = closing ? &file->data->str[2] : &file->data->str[1];
+        for (dst = tag; dst < end; dst++)
+            if (_is_space(*dst))
+                break;
+        _update_file_ptr_part(file, file->data->str, dst + 1);
+        *dst = '\0'; /* terminate the tag */
+        if (closing)
+        {
+            if (dst != end) /* we got a space char in closing tag */
+            {
+                g_set_error_literal(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                                    _("Space isn't allowed in the close tag"));
+                return FALSE;
+            }
+            /* g_debug("XML parser: found closing tag '%s' for %p at %d:%d", tag,
+                    file->current_item, file->line, file->pos); */
+            item = file->current_item;
+            if (item == NULL) /* no tag to close */
+            {
+                g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                            _("Element '%s' was closed but no element was opened"),
+                            tag);
+                return FALSE;
+            }
+            else
+            {
+                char *tagname;
+
+                if (item->tag == FM_XML_FILE_TAG_NOT_HANDLED)
+                    tagname = item->tag_name;
+                else
+                    tagname = file->tags[item->tag].name;
+                if (strcmp(tag, tagname)) /* closing tag doesn't match */
+                {
+                    /* FIXME: validate tag so be more verbose on error */
+                    g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                                _("Element '%s' was closed but the currently "
+                                  "open element is '%s'"), tag, tagname);
+                    return FALSE;
+                }
+                file->current_item = item->parent;
+_close_the_tag:
+                /* g_debug("XML parser: close the tag '%s'", tag); */
+                g_string_truncate(file->data, 0);
+                if (item->tag != FM_XML_FILE_TAG_NOT_HANDLED)
+                {
+                    if (!file->tags[item->tag].handler(item, item->children,
+                                                       item->attribute_names,
+                                                       item->attribute_values,
+                                                       item->attribute_names ? g_strv_length(item->attribute_names) : 0,
+                                                       file->line,
+                                                       file->pos,
+                                                       error, user_data))
+                        return FALSE;
+                }
+                file->pos++; /* '>' */
+                goto _restart;
+            }
+        }
+        else /* opening tag */
+        {
+            /* g_debug("XML parser: found opening tag '%s'", tag); */
+            /* parse and check tag name */
+            for (i = 1; i < file->n_tags; i++)
+                if (strcmp(file->tags[i].name, tag) == 0)
+                    break;
+            if (i == file->n_tags)
+                /* FIXME: do name validation */
+                i = FM_XML_FILE_TAG_NOT_HANDLED;
+            /* parse and check attributes */
+            attribs = 0;
+            attrib_names = attrib_values = NULL;
+            while (dst < end)
+            {
+                name = &dst[1]; /* skip this space */
+                while (name < end && _is_space(*name))
+                    name++;
+                value = name;
+                while (value < end && !_is_space(*value) && *value != '=')
+                    value++;
+                len = value - name;
+                _update_file_ptr_part(file, dst, value);
+                /* FIXME: skip spaces before =? */
+                if (value + 3 <= end && *value == '=') /* minimum is ="" */
+                {
+                    value++;
+                    file->pos++; /* '=' */
+                    /* FIXME: skip spaces after =? */
+                    quote = *value++;
+                    if (quote != '\'' && quote != '"')
+                    {
+                        g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                                    _("Invalid char '%c' at start of attribute value"),
+                                    quote);
+                        goto _attr_error;
+                    }
+                    file->pos++; /* quote char */
+                    for (ptr = 0; &value[ptr] < end; ptr++)
+                        if (value[ptr] == quote)
+                            break;
+                    if (&value[ptr] == end)
+                    {
+                        g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                                    _("Invalid char '%c' at end of attribute value,"
+                                      " expected '%c'"), value[ptr-1], quote);
+                        goto _attr_error;
+                    }
+                    buff = g_string_new_len(value, ptr);
+                    if (!unescape_gstring_inplace(buff, &file->line,
+                                                  &file->pos, TRUE, error))
+                    {
+                        g_string_free(buff, TRUE);
+_attr_error:
+                        for (i = 0; i < attribs; i++)
+                        {
+                            g_free(attrib_names[i]);
+                            g_free(attrib_values[i]);
+                        }
+                        g_free(attrib_names);
+                        g_free(attrib_values);
+                        return FALSE;
+                    }
+                    dst = &value[ptr+1];
+                    value = g_string_free(buff, FALSE);
+                    file->pos++; /* end quote char */
+                }
+                else
+                {
+                    dst = value;
+                    value = NULL;
+                    /* FIXME: isn't it error? */
+                }
+                attrib_names = g_renew(char *, attrib_names, attribs + 2);
+                attrib_values = g_renew(char *, attrib_values, attribs + 2);
+                attrib_names[attribs] = g_strndup(name, len);
+                attrib_values[attribs] = value;
+                attribs++;
+            }
+            if (attribs > 0)
+            {
+                attrib_names[attribs] = NULL;
+                attrib_values[attribs] = NULL;
+            }
+            /* create new item */
+            item = fm_xml_file_item_new(i);
+            item->attribute_names = attrib_names;
+            item->attribute_values = attrib_values;
+            if (i == FM_XML_FILE_TAG_NOT_HANDLED)
+                item->tag_name = g_strdup(tag);
+            /* insert new item into the container */
+            item->comment = file->comment_pre;
+            file->comment_pre = NULL;
+            if (file->current_item)
+                fm_xml_file_item_append_child(file->current_item, item);
+            else
+            {
+                item->file = file;
+                item->parent_list = &file->items;
+                file->items = g_list_append(file->items, item);
+            }
+            file->pos++; /* '>' or '/' */
+            if (selfdo) /* simple self-closing tag */
+                goto _close_the_tag;
+            file->current_item = item;
+            g_string_truncate(file->data, 0);
+            goto _restart;
+        }
+    }
+    /* otherwise we stopped at some data somewhere */
+    else
+    {
+        if (!file->data || file->data->len == 0) while (size > 0)
+        {
+            /* skip leading spaces */
+            if (*text == '\n')
+            {
+                file->line++;
+                file->pos = 0;
+            }
+            else if (*text == ' ' || *text == '\t' || *text == '\r')
+                file->pos++;
+            else
+                break;
+            text++;
+            size--;
+        }
+        for (ptr = 0; ptr < size; ptr++)
+            if (text[ptr] == '<')
+                break;
+        if (file->data == NULL)
+            file->data = g_string_new_len(text, ptr);
+        else if (ptr > 0)
+            g_string_append_len(file->data, text, ptr);
+        if (ptr == size) /* still no end of text */
+            return TRUE;
+        /* if(ptr>0) g_debug("XML parser: got text '%s'", file->data->str); */
+        if (ptr == 0) ; /* no text */
+        else if (file->current_item == NULL) /* text at top level! */
+        {
+            g_warning("FmXmlFile: line %u: junk data in XML file ignored",
+                      file->line);
+            _update_file_ptr(file, 0);
+        }
+        else if (unescape_gstring_inplace(file->data, &file->line,
+                                          &file->pos, FALSE, error))
+        {
+            item = fm_xml_file_item_new(FM_XML_FILE_TEXT);
+            item->text = g_strndup(file->data->str, file->data->len);
+            item->comment = file->comment_pre;
+            file->comment_pre = NULL;
+            fm_xml_file_item_append_child(file->current_item, item);
+            /* FIXME: truncate ending spaces from item->text */
+        }
+        else
+            return FALSE;
+        ptr++;
+        text += ptr;
+        size -= ptr;
+        g_string_assign(file->data, "<");
+        goto _restart;
+    }
+    /* error if reached */
+}
+
+/**
+ * fm_xml_file_finish_parse
+ * @file: the parser container
+ * @error: (allow-none) (out): location to save error
+ *
+ * Ends parsing of data and retrieves final status. If XML was invalid
+ * then returns %NULL and sets @error appropriately.
+ * This function can be called more than once.
+ *
+ * See also: fm_xml_file_parse_data().
+ * See also: fm_xml_file_item_get_children().
+ *
+ * Returns: (transfer container) (element-type FmXmlFileItem): contents of XML
+ *
+ * Since: 1.2.0
+ */
+GList *fm_xml_file_finish_parse(FmXmlFile *file, GError **error)
+{
+    g_return_val_if_fail(file != NULL && FM_IS_XML_FILE(file), NULL);
+    if (file->current_item)
+    {
+        if (file->current_item->tag == FM_XML_FILE_TEXT &&
+            file->current_item->parent == NULL)
+            g_warning("FmXmlFile: junk at end of XML");
+        else
+        {
+            g_set_error_literal(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
+                                _("Document ended unexpectedly"));
+            /* FIXME: analize content of file->data to be more verbose */
+            return NULL;
+        }
+    }
+    else if (file->items == NULL)
+    {
+        g_set_error_literal(error, G_MARKUP_ERROR, G_MARKUP_ERROR_EMPTY,
+                            _("Document was empty or contained only whitespace"));
+        return NULL;
+    }
+    /* FIXME: check if file->comment_pre is NULL */
+    return g_list_copy(file->items);
+}
+
+/**
+ * fm_xml_file_get_current_line
+ * @file: the parser container
+ * @pos: (allow-none) (out): location to save line position
+ *
+ * Retrieves the line where parser has stopped.
+ *
+ * Returns: line num (starting from 1).
+ *
+ * Since: 1.2.0
+ */
+gint fm_xml_file_get_current_line(FmXmlFile *file, gint *pos)
+{
+    if (file == NULL || !FM_IS_XML_FILE(file))
+        return 0;
+    if (pos)
+        *pos = file->pos;
+    return file->line;
+}
+
+/**
+ * fm_xml_file_get_dtd
+ * @file: the parser container
+ *
+ * Retrieves DTD description for XML data in the container. Returned data
+ * are owned by @file and should not be modified by caller.
+ *
+ * Returns: (transfer none): DTD description.
+ *
+ * Since: 1.2.0
+ */
+const char *fm_xml_file_get_dtd(FmXmlFile *file)
+{
+    if(file == NULL)
+        return NULL;
+    return file->tags[0].name;
+}
+
+
+/* item manipulations */
+
+/**
+ * fm_xml_file_item_new
+ * @tag: tag id for new item
+ *
+ * Creates new unattached XML item.
+ *
+ * Returns: (transfer full): newly allocated #FmXmlFileItem.
+ *
+ * Since: 1.2.0
+ */
+FmXmlFileItem *fm_xml_file_item_new(FmXmlFileTag tag)
+{
+    FmXmlFileItem *item = g_slice_new0(FmXmlFileItem);
+
+    item->tag = tag;
+    return item;
+}
+
+/**
+ * fm_xml_file_item_append_text
+ * @item: item to append text
+ * @text: text to append
+ * @text_size: length of text in bytes, or -1 if the text is nul-terminated
+ * @cdata: %TRUE if @text should be saved as CDATA array
+ *
+ * Appends @text after last element contained in @item.
+ *
+ * Since: 1.2.0
+ */
+void fm_xml_file_item_append_text(FmXmlFileItem *item, const char *text,
+                                  gssize text_size, gboolean cdata)
+{
+    FmXmlFileItem *text_item;
+
+    g_return_if_fail(item != NULL);
+    if (text == NULL || text_size == 0)
+        return;
+    text_item = fm_xml_file_item_new(FM_XML_FILE_TEXT);
+    if (text_size > 0)
+        text_item->text = g_strndup(text, text_size);
+    else
+        text_item->text = g_strdup(text);
+    if (cdata)
+        text_item->comment = text_item->text;
+    fm_xml_file_item_append_child(item, text_item);
+}
+
+static inline gboolean _xml_item_is_busy(FmXmlFileItem *item)
+{
+    register FmXmlFileItem *test;
+
+    if (item->file)
+        for (test = item->file->current_item; test; test = test->parent)
+            if (test == item)
+            {
+                /* g_debug("*** item %p is busy", item); */
+                return TRUE;
+            }
+    return FALSE;
+}
+
+static void _reassign_xml_file(FmXmlFileItem *item, FmXmlFile *file)
+{
+    GList *chl;
+
+    /* do it recursively */
+    for (chl = item->children; chl; chl = chl->next)
+        _reassign_xml_file(chl->data, file);
+    item->file = file;
+}
+
+/**
+ * fm_xml_file_item_append_child
+ * @item: item to append child
+ * @child: the child item to append
+ *
+ * Appends @child after last element contained in @item. If the @child
+ * already was in the XML structure then it will be moved to the new
+ * place instead.
+ * Behavior after moving between different containers is undefined.
+ *
+ * Returns: %FALSE if @child is busy thus cannot be moved.
+ *
+ * Since: 1.2.0
+ */
+gboolean fm_xml_file_item_append_child(FmXmlFileItem *item, FmXmlFileItem *child)
+{
+    g_return_val_if_fail(item != NULL && child != NULL, FALSE);
+    if (_xml_item_is_busy(child))
+        return FALSE; /* cannot move item right now */
+    if (child->parent_list) /* remove from old list */
+    {
+        /* g_debug("moving item %p(%d) from parser %p into %p as child of %p", child, (int)child->tag, child->file, item->file, item); */
+        g_assert(child->file != NULL && g_list_find(*child->parent_list, child) != NULL);
+        *child->parent_list = g_list_remove(*child->parent_list, child);
+    }
+    /* else
+        g_debug("adding item %p(%d) into parser %p as child of %p", child, (int)child->tag, item->file, item); */
+    item->children = g_list_append(item->children, child);
+    child->parent_list = &item->children;
+    child->parent = item;
+    if (child->file != item->file)
+        _reassign_xml_file(child, item->file);
+    return TRUE;
+}
+
+/**
+ * fm_xml_file_item_set_comment
+ * @item: element to set
+ * @comment: (allow-none): new comment
+ *
+ * Changes comment that is prepended to @item.
+ *
+ * Since: 1.2.0
+ */
+void fm_xml_file_item_set_comment(FmXmlFileItem *item, const char *comment)
+{
+    g_return_if_fail(item != NULL);
+    g_free(item->comment);
+    item->comment = g_strdup(comment);
+}
+
+/**
+ * fm_xml_file_item_set_attribute
+ * @item: element to update
+ * @name: attribute name
+ * @value: (allow-none): attribute data
+ *
+ * Changes data for the attribute of some @item with new @value. If such
+ * attribute wasn't set then adds it for the @item. If @value is %NULL
+ * then the attribute will be unset from the @item.
+ *
+ * Returns: %TRUE if attribute was set successfully.
+ *
+ * Since: 1.2.0
+ */
+gboolean fm_xml_file_item_set_attribute(FmXmlFileItem *item,
+                                        const char *name, const char *value)
+{
+    int i, n_attr;
+
+    g_return_val_if_fail(item != NULL, FALSE);
+    g_return_val_if_fail(name != NULL, FALSE);
+    if (item->attribute_names == NULL && value == NULL)
+        return TRUE;
+    if (item->attribute_names == NULL)
+    {
+        item->attribute_names = g_new(char *, 2);
+        item->attribute_values = g_new(char *, 2);
+        item->attribute_names[0] = g_strdup(name);
+        item->attribute_values[0] = g_strdup(value);
+        item->attribute_names[1] = NULL;
+        item->attribute_values[1] = NULL;
+        return TRUE;
+    }
+    for (i = -1, n_attr = 0; item->attribute_names[n_attr] != NULL; n_attr++)
+        if (strcmp(item->attribute_names[n_attr], name) == 0)
+            i = n_attr;
+    if (i < 0)
+    {
+        if (value == NULL) /* already unset */
+            return TRUE;
+        item->attribute_names = g_renew(char *, item->attribute_names, n_attr + 2);
+        item->attribute_values = g_renew(char *, item->attribute_values, n_attr + 2);
+        item->attribute_names[n_attr] = g_strdup(name);
+        item->attribute_values[n_attr] = g_strdup(value);
+        item->attribute_names[n_attr+1] = NULL;
+        item->attribute_values[n_attr+1] = NULL;
+    }
+    else if (value != NULL) /* value changed */
+    {
+        g_free(item->attribute_values[i]);
+        item->attribute_values[i] = g_strdup(value);
+    }
+    else if (n_attr == 1) /* no more attributes left */
+    {
+        g_strfreev(item->attribute_names);
+        g_strfreev(item->attribute_values);
+        item->attribute_names = NULL;
+        item->attribute_values = NULL;
+    }
+    else /* replace removed attribute with last one if it wasn't last */
+    {
+        g_free(item->attribute_names[i]);
+        g_free(item->attribute_values[i]);
+        if (i < n_attr - 1)
+        {
+            item->attribute_names[i] = item->attribute_names[n_attr-1];
+            item->attribute_values[i] = item->attribute_values[n_attr-1];
+        }
+        item->attribute_names[n_attr-1] = NULL;
+        item->attribute_values[n_attr-1] = NULL;
+    }
+    return TRUE;
+}
+
+/**
+ * fm_xml_file_item_destroy
+ * @item: element to destroy
+ *
+ * Removes element and its children from its parent, and frees all
+ * data.
+ *
+ * Returns: %FALSE if @item is busy thus cannot be destroyed.
+ *
+ * Since: 1.2.0
+ */
+gboolean fm_xml_file_item_destroy(FmXmlFileItem *item)
+{
+    g_return_val_if_fail(item != NULL, FALSE);
+    if (_xml_item_is_busy(item))
+        return FALSE;
+    while (item->children)
+    {
+        g_assert(((FmXmlFileItem*)item->children->data)->file == item->file);
+        g_assert(((FmXmlFileItem*)item->children->data)->parent == item);
+        fm_xml_file_item_destroy(item->children->data);
+    }
+    if (item->parent_list)
+    {
+        /* g_debug("removing item %p from parser %p", item, item->file); */
+        g_assert(item->file != NULL && g_list_find(*item->parent_list, item) != NULL);
+        *item->parent_list = g_list_remove(*item->parent_list, item);
+    }
+    if (item->text != item->comment)
+        g_free(item->comment);
+    g_free(item->text);
+    g_strfreev(item->attribute_names);
+    g_strfreev(item->attribute_values);
+    g_slice_free(FmXmlFileItem, item);
+    return TRUE;
+}
+
+/**
+ * fm_xml_file_insert_before
+ * @item: item to insert before it
+ * @new_item: new item to insert
+ *
+ * Inserts @new_item before @item that is already in XML structure. If
+ * @new_item is already in the XML structure then it will be moved to
+ * the new place instead.
+ * Behavior after moving between defferent containers is undefined.
+ *
+ * Returns: %TRUE in case of success.
+ *
+ * Since: 1.2.0
+ */
+gboolean fm_xml_file_insert_before(FmXmlFileItem *item, FmXmlFileItem *new_item)
+{
+    GList *sibling;
+
+    g_return_val_if_fail(item != NULL && new_item != NULL, FALSE);
+    sibling = g_list_find(*item->parent_list, item);
+    if (sibling == NULL) /* no such item found */
+    {
+        /* g_critical("item %p not found in %p", item, item->parent); */
+        return FALSE;
+    }
+    if (_xml_item_is_busy(new_item))
+        return FALSE; /* cannot move item right now */
+    if (new_item->parent_list) /* remove from old list */
+    {
+        /* g_debug("moving item %p (parent=%p) from parser %p into %p", new_item, item, new_item->file, item->file); */
+        g_assert(new_item->file != NULL && g_list_find(*new_item->parent_list, new_item) != NULL);
+        *new_item->parent_list = g_list_remove(*new_item->parent_list, new_item);
+    }
+    /* else
+        g_debug("inserting item %p (parent=%p) into parser %p", item, new_item, item->file); */
+    *item->parent_list = g_list_insert_before(*item->parent_list, sibling, new_item);
+    new_item->parent_list = item->parent_list;
+    new_item->parent = item->parent;
+    if (new_item->file != item->file)
+        _reassign_xml_file(new_item, item->file);
+    return TRUE;
+}
+
+/**
+ * fm_xml_file_insert_first
+ * @file: the parser container
+ * @new_item: new item to insert
+ *
+ * Inserts @new_item as very first element of XML data in container.
+ *
+ * Returns: %TRUE in case of success.
+ *
+ * Since: 1.2.0
+ */
+gboolean fm_xml_file_insert_first(FmXmlFile *file, FmXmlFileItem *new_item)
+{
+    g_return_val_if_fail(file != NULL && FM_IS_XML_FILE(file), FALSE);
+    g_return_val_if_fail(new_item != NULL, FALSE);
+    if (_xml_item_is_busy(new_item))
+        return FALSE; /* cannot move item right now */
+    if (new_item->parent_list)
+    {
+        /* g_debug("moving item %p from parser %p into %p", new_item, new_item->file, file); */
+        g_assert(new_item->file != NULL && g_list_find(*new_item->parent_list, new_item) != NULL);
+        *new_item->parent_list = g_list_remove(*new_item->parent_list, new_item);
+    }
+    file->items = g_list_prepend(file->items, new_item);
+    new_item->parent_list = &file->items;
+    new_item->parent = NULL;
+    if (new_item->file != file)
+        _reassign_xml_file(new_item, file);
+    return TRUE;
+}
+
+
+/* save XML */
+
+/**
+ * fm_xml_file_set_dtd
+ * @file: the parser container
+ * @dtd: DTD description for XML data
+ * @error: (allow-none) (out): location to save error
+ *
+ * Changes DTD description for XML data in the container.
+ *
+ * Since: 1.2.0
+ */
+void fm_xml_file_set_dtd(FmXmlFile *file, const char *dtd, GError **error)
+{
+    if(file == NULL)
+        return;
+    /* FIXME: validate dtd */
+    g_free(file->tags[0].name);
+    file->tags[0].name = g_strdup(dtd);
+}
+
+static gboolean _parser_item_to_gstring(FmXmlFile *file, GString *string,
+                                        FmXmlFileItem *item, GString *prefix,
+                                        gboolean *has_nl, GError **error)
+{
+    const char *tag_name;
+    GList *l;
+
+    /* open the tag */
+    switch (item->tag)
+    {
+    case FM_XML_FILE_TAG_NOT_HANDLED:
+        if (item->tag_name == NULL)
+            goto _no_tag;
+        tag_name = item->tag_name;
+        goto _do_tag;
+    case FM_XML_FILE_TEXT:
+        if (item->text == item->comment) /* CDATA */
+            g_string_append_printf(string, "<![CDATA[%s]]>", item->text);
+        else if (item->text) /* just text */
+        {
+            char *escaped;
+
+            if (item->comment != NULL)
+                g_string_append_printf(string, "<!-- %s -->", item->comment);
+            escaped = g_markup_escape_text(item->text, -1);
+            g_string_append(string, escaped);
+            g_free(escaped);
+        }
+        else /* processing directive */
+        {
+            g_string_append_printf(string, "%s<?%s?>", prefix->str, item->comment);
+            *has_nl = TRUE;
+        }
+        return TRUE;
+    default:
+        if (item->tag >= file->n_tags)
+        {
+_no_tag:
+            g_set_error_literal(error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+                                _("fm_xml_file_to_data: XML data error"));
+            return FALSE;
+        }
+        tag_name = file->tags[item->tag].name;
+_do_tag:
+        /* do comment */
+        if (item->comment != NULL)
+            g_string_append_printf(string, "%s<!-- %s -->", prefix->str,
+                                   item->comment);
+        else if (item->attribute_names == NULL && item->children == NULL &&
+                 file->tags[item->tag].in_line)
+        {
+            /* don't add prefix if it is simple tag such as <br/> */
+            g_string_append_printf(string, "<%s/>", tag_name);
+            return TRUE;
+        }
+        /* start the tag */
+        g_string_append_printf(string, "%s<%s", prefix->str, tag_name);
+        /* do attributes */
+        if (item->attribute_names)
+        {
+            char **name = item->attribute_names;
+            char **value = item->attribute_values;
+            while (*name)
+            {
+                if (*value)
+                {
+                    char *escaped = g_markup_escape_text(*value, -1);
+                    g_string_append_printf(string, " %s='%s'", *name, escaped);
+                    g_free(escaped);
+                } /* else error? */
+                name++;
+                value++;
+            }
+        }
+        if (item->children == NULL)
+        {
+            /* handle empty tags such as <tag attr='value'/> */
+            g_string_append(string, "/>");
+            *has_nl = TRUE;
+            return TRUE;
+        }
+        g_string_append_c(string, '>');
+    }
+    /* do with children */
+    *has_nl = FALSE; /* to collect data from nested elements */
+    g_string_append(prefix, "    ");
+    for (l = item->children; l; l = l->next)
+        if (!_parser_item_to_gstring(file, string, l->data, prefix, has_nl, error))
+            break;
+    g_string_truncate(prefix, prefix->len - 4);
+    if (l != NULL) /* failed */
+        return FALSE;
+    /* close the tag */
+    g_string_append_printf(string, "%s</%s>", (*has_nl) ? prefix->str : "",
+                           tag_name);
+    *has_nl = TRUE; /* it was prefixed above */
+    return TRUE;
+}
+
+/**
+ * fm_xml_file_to_data
+ * @file: the parser container
+ * @text_size: (allow-none) (out): location to save size of returned data
+ * @error: (allow-none) (out): location to save error
+ *
+ * Prepares string representation (XML text) for the data that are in
+ * the container. Returned data should be freed with g_free() after
+ * usage.
+ *
+ * Returns: (transfer full): XML text representing data in @file.
+ *
+ * Since: 1.2.0
+ */
+char *fm_xml_file_to_data(FmXmlFile *file, gsize *text_size, GError **error)
+{
+    GString *string, *prefix;
+    GList *l;
+    gboolean has_nl = FALSE;
+
+    g_return_val_if_fail(file != NULL && FM_IS_XML_FILE(file), NULL);
+    string = g_string_sized_new(512);
+    prefix = g_string_new("\n");
+    if (G_LIKELY(file->tags[0].name))
+        g_string_printf(string, "<!DOCTYPE %s>", file->tags[0].name);
+    for (l = file->items; l; l = l->next)
+        if (!_parser_item_to_gstring(file, string, l->data, prefix, &has_nl, error))
+            break; /* if failed then l != NULL */
+    g_string_free(prefix, TRUE);
+    if (text_size)
+        *text_size = string->len;
+    return g_string_free(string, (l != NULL)); /* returns NULL if failed */
+}
+
+
+/* item data accessor functions */
+
+/**
+ * fm_xml_file_item_get_comment
+ * @item: the file element to inspect
+ *
+ * If an element @item has a comment ahead of it then retrieves that
+ * comment. The returned data are owned by @item and should not be freed
+ * nor otherwise altered by caller.
+ *
+ * Returns: (transfer none): comment or %NULL if no comment is set.
+ *
+ * Since: 1.2.0
+ */
+const char *fm_xml_file_item_get_comment(FmXmlFileItem *item)
+{
+    g_return_val_if_fail(item != NULL, NULL);
+    return item->comment;
+}
+
+/**
+ * fm_xml_file_item_get_children
+ * @item: the file element to inspect
+ *
+ * Retrieves list of children for @item that are known to the parser.
+ * Returned list should be freed by g_list_free() after usage.
+ *
+ * Note: any text between opening tag and closing tag such as
+ * |[ &lt;Tag&gt;Some text&lt;/Tag&gt; ]|
+ * is presented as a child of special type #FM_XML_FILE_TEXT and can be
+ * retrieved from that child, not from the item representing &lt;Tag&gt;.
+ *
+ * See also: fm_xml_file_item_get_data().
+ *
+ * Returns: (transfer container) (element-type FmXmlFileItem): children list.
+ *
+ * Since: 1.2.0
+ */
+GList *fm_xml_file_item_get_children(FmXmlFileItem *item)
+{
+    g_return_val_if_fail(item != NULL, NULL);
+    return g_list_copy(item->children);
+}
+
+/**
+ * fm_xml_file_item_find_child
+ * @item: the file element to inspect
+ * @tag: tag id to search among children
+ *
+ * Searches for first child of @item which have child with tag id @tag.
+ * Returned data are owned by file and should not be freed by caller.
+ *
+ * Returns: (transfer none): found child or %NULL if no such child was found.
+ *
+ * Since: 1.2.0
+ */
+FmXmlFileItem *fm_xml_file_item_find_child(FmXmlFileItem *item, FmXmlFileTag tag)
+{
+    GList *l;
+
+    for (l = item->children; l; l = l->next)
+        if (((FmXmlFileItem*)l->data)->tag == tag)
+            return (FmXmlFileItem*)l->data;
+    return NULL;
+}
+
+/**
+ * fm_xml_file_item_get_tag
+ * @item: the file element to inspect
+ *
+ * Retrieves tag id of @item.
+ *
+ * Returns: tag id.
+ *
+ * Since: 1.2.0
+ */
+FmXmlFileTag fm_xml_file_item_get_tag(FmXmlFileItem *item)
+{
+    g_return_val_if_fail(item != NULL, FM_XML_FILE_TAG_NOT_HANDLED);
+    return item->tag;
+}
+
+/**
+ * fm_xml_file_item_get_data
+ * @item: the file element to inspect
+ * @text_size: (allow-none) (out): location to save data size
+ *
+ * Retrieves text data from @item of type #FM_XML_FILE_TEXT. Returned
+ * data are owned by file and should not be freed nor altered.
+ *
+ * Returns: (transfer none): text data or %NULL if @item isn't text data.
+ *
+ * Since: 1.2.0
+ */
+const char *fm_xml_file_item_get_data(FmXmlFileItem *item, gsize *text_size)
+{
+    if (text_size)
+        *text_size = 0;
+    g_return_val_if_fail(item != NULL, NULL);
+    if (item->tag != FM_XML_FILE_TEXT)
+        return NULL;
+    if (text_size && item->text != NULL)
+        *text_size = strlen(item->text);
+    return item->text;
+}
+
+/**
+ * fm_xml_file_item_get_parent
+ * @item: the file element to inspect
+ *
+ * Retrieves parent element of @item if the @item has one. Returned data
+ * are owned by file and should not be freed by caller.
+ *
+ * Returns: (transfer none): parent element or %NULL if element has no parent.
+ *
+ * Since: 1.2.0
+ */
+FmXmlFileItem *fm_xml_file_item_get_parent(FmXmlFileItem *item)
+{
+    g_return_val_if_fail(item != NULL, NULL);
+    return item->parent;
+}
+
+/**
+ * fm_xml_file_get_tag_name
+ * @file: the parser container
+ * @tag: the tag id to inspect
+ *
+ * Retrieves tag for its id. Returned data are owned by @file and should
+ * not be modified by caller.
+ *
+ * Returns: (transfer none): tag string representation.
+ *
+ * Since: 1.2.0
+ */
+const char *fm_xml_file_get_tag_name(FmXmlFile *file, FmXmlFileTag tag)
+{
+    g_return_val_if_fail(file != NULL && FM_IS_XML_FILE(file), NULL);
+    g_return_val_if_fail(tag > 0 && tag < file->n_tags, NULL);
+    return file->tags[tag].name;
+}
+
+/**
+ * fm_xml_file_item_get_tag_name
+ * @item: the file element to inspect
+ *
+ * Retrieves tag for its id. Returned data are owned by @item and should
+ * not be modified by caller.
+ *
+ * Returns: (transfer none): tag string representation or %NULL if @item is text.
+ *
+ * Since: 1.2.0
+ */
+const char *fm_xml_file_item_get_tag_name(FmXmlFileItem *item)
+{
+    g_return_val_if_fail(item != NULL, NULL);
+    switch (item->tag)
+    {
+    case FM_XML_FILE_TEXT:
+        return NULL;
+    case FM_XML_FILE_TAG_NOT_HANDLED:
+        return item->tag_name;
+    default:
+        return item->file->tags[item->tag].name;
+    }
+}
diff --git a/src/core/vfs/fm-xml-file.h b/src/core/vfs/fm-xml-file.h
new file mode 100644 (file)
index 0000000..e31f332
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ *      fm-xml-file.h
+ *
+ *      Copyright 2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ *      This file is a part of libfm-extra package.
+ *
+ *      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_XML_FILE_H__
+#define __FM_XML_FILE_H__ 1
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define FM_XML_FILE_TYPE           (fm_xml_file_get_type())
+#define FM_IS_XML_FILE(obj)        (G_TYPE_CHECK_INSTANCE_TYPE((obj), FM_XML_FILE_TYPE))
+
+typedef struct _FmXmlFile           FmXmlFile;
+typedef struct _FmXmlFileClass      FmXmlFileClass;
+
+GType fm_xml_file_get_type(void);
+
+typedef struct _FmXmlFileItem       FmXmlFileItem;
+typedef guint                       FmXmlFileTag;
+
+/**
+ * FM_XML_FILE_TAG_NOT_HANDLED:
+ *
+ * Value of FmXmlFileTag which means this element has no handler installed.
+ */
+#define FM_XML_FILE_TAG_NOT_HANDLED 0
+
+/**
+ * FM_XML_FILE_TEXT:
+ *
+ * Value of FmXmlFileTag which means this element has parsed character data.
+ */
+#define FM_XML_FILE_TEXT (FmXmlFileTag)-1
+
+/**
+ * FmXmlFileHandler
+ * @item: XML element being parsed
+ * @children: (element-type FmXmlFileItem): elements found in @item
+ * @attribute_names: attributes names list for @item
+ * @attribute_values: attributes values list for @item
+ * @n_attributes: list length of @attribute_names and @attribute_values
+ * @line: current line number in the file (starting from 1)
+ * @pos: current pos number in the file (starting from 0)
+ * @error: (allow-none) (out): location to save error
+ * @user_data: data passed to fm_xml_file_parse_data()
+ *
+ * Callback for processing some element in XML file.
+ * It will be called at closing tag.
+ *
+ * Returns: %TRUE if no errors were found by handler.
+ *
+ * Since: 1.2.0
+ */
+typedef gboolean (*FmXmlFileHandler)(FmXmlFileItem *item, GList *children,
+                                     char * const *attribute_names,
+                                     char * const *attribute_values,
+                                     guint n_attributes, gint line, gint pos,
+                                     GError **error, gpointer user_data);
+
+/* setup */
+FmXmlFile *fm_xml_file_new(FmXmlFile *sibling);
+FmXmlFileTag fm_xml_file_set_handler(FmXmlFile *file, const char *tag,
+                                     FmXmlFileHandler handler, gboolean in_line,
+                                     GError **error);
+
+/* parse */
+gboolean fm_xml_file_parse_data(FmXmlFile *file, const char *text,
+                                gsize size, GError **error, gpointer user_data);
+GList *fm_xml_file_finish_parse(FmXmlFile *file, GError **error);
+gint fm_xml_file_get_current_line(FmXmlFile *file, gint *pos);
+const char *fm_xml_file_get_dtd(FmXmlFile *file);
+
+/* item manipulations */
+FmXmlFileItem *fm_xml_file_item_new(FmXmlFileTag tag);
+void fm_xml_file_item_append_text(FmXmlFileItem *item, const char *text,
+                                  gssize text_size, gboolean cdata);
+gboolean fm_xml_file_item_append_child(FmXmlFileItem *item, FmXmlFileItem *child);
+void fm_xml_file_item_set_comment(FmXmlFileItem *item, const char *comment);
+gboolean fm_xml_file_item_set_attribute(FmXmlFileItem *item,
+                                        const char *name, const char *value);
+gboolean fm_xml_file_item_destroy(FmXmlFileItem *item);
+
+gboolean fm_xml_file_insert_before(FmXmlFileItem *item, FmXmlFileItem *new_item);
+gboolean fm_xml_file_insert_first(FmXmlFile *file, FmXmlFileItem *new_item);
+
+/* save XML */
+void fm_xml_file_set_dtd(FmXmlFile *file, const char *dtd, GError **error);
+char *fm_xml_file_to_data(FmXmlFile *file, gsize *text_size, GError **error);
+
+/* item data accessor functions */
+const char *fm_xml_file_item_get_comment(FmXmlFileItem *item);
+GList *fm_xml_file_item_get_children(FmXmlFileItem *item);
+FmXmlFileItem *fm_xml_file_item_find_child(FmXmlFileItem *item, FmXmlFileTag tag);
+FmXmlFileTag fm_xml_file_item_get_tag(FmXmlFileItem *item);
+const char *fm_xml_file_item_get_data(FmXmlFileItem *item, gsize *text_size);
+FmXmlFileItem *fm_xml_file_item_get_parent(FmXmlFileItem *item);
+const char *fm_xml_file_item_get_tag_name(FmXmlFileItem *item);
+const char *fm_xml_file_get_tag_name(FmXmlFile *file, FmXmlFileTag tag);
+
+G_END_DECLS
+
+#endif /* __FM_XML_FILE_H__ */
diff --git a/src/core/vfs/vfs-menu.c b/src/core/vfs/vfs-menu.c
new file mode 100644 (file)
index 0000000..12289ee
--- /dev/null
@@ -0,0 +1,3137 @@
+/*
+ *      fm-vfs-menu.c
+ *      VFS for "menu://applications/" path using menu-cache library.
+ *
+ *      Copyright 2012-2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "fm-file.h"
+#include "glib-compat.h"
+// #include "fm-utils.h"
+#include "fm-xml-file.h"
+
+#include <glib/gi18n-lib.h>
+#include <menu-cache.h>
+
+/* support for libmenu-cache 0.4.x */
+#ifndef MENU_CACHE_CHECK_VERSION
+# ifdef HAVE_MENU_CACHE_DIR_LIST_CHILDREN
+#  define MENU_CACHE_CHECK_VERSION(_a,_b,_c) (_a == 0 && _b < 5) /* < 0.5.0 */
+# else
+#  define MENU_CACHE_CHECK_VERSION(_a,_b,_c) 0 /* not even 0.4.0 */
+# endif
+#endif
+
+/* libmenu-cache is multithreaded since 0.4.x */
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+# define RUN_WITH_MENU_CACHE(__func,__data) __func(__data)
+#else
+# define RUN_WITH_MENU_CACHE(__func,__data) fm_run_in_default_main_context(__func,__data)
+#endif
+
+/* beforehand declarations */
+GFile *_fm_vfs_menu_new_for_uri(const char *uri);
+
+
+/* ---- applications.menu manipulations ---- */
+typedef struct _FmMenuMenuTree          FmMenuMenuTree;
+
+struct _FmMenuMenuTree
+{
+    FmXmlFile *menu; /* composite tree to analyze */
+    char *file_path; /* current file */
+    GCancellable *cancellable;
+    gint line, pos; /* we remember position in deepest file */
+};
+
+G_LOCK_DEFINE_STATIC(menuTree); /* locks all .menu file access data below */
+static FmXmlFileTag menuTag_Menu = 0; /* tags that are supported */
+static FmXmlFileTag menuTag_Include = 0;
+static FmXmlFileTag menuTag_Exclude = 0;
+static FmXmlFileTag menuTag_Filename = 0;
+static FmXmlFileTag menuTag_Category = 0;
+static FmXmlFileTag menuTag_MergeFile = 0;
+static FmXmlFileTag menuTag_Directory = 0;
+static FmXmlFileTag menuTag_Name = 0;
+static FmXmlFileTag menuTag_Deleted = 0;
+static FmXmlFileTag menuTag_NotDeleted = 0;
+
+/* this handler does nothing, used just to remember its id */
+static gboolean _menu_xml_handler_pass(FmXmlFileItem *item, GList *children,
+                                       char * const *attribute_names,
+                                       char * const *attribute_values,
+                                       guint n_attributes, gint line, gint pos,
+                                       GError **error, gpointer user_data)
+{
+    FmMenuMenuTree *data = user_data;
+    return !g_cancellable_set_error_if_cancelled(data->cancellable, error);
+}
+
+/* FIXME: handle <Move><Old>...</Old><New>...</New></Move> */
+
+static inline const char *_get_menu_name(FmXmlFileItem *item)
+{
+    if (fm_xml_file_item_get_tag(item) != menuTag_Menu) /* skip not menu */
+        return NULL;
+    item = fm_xml_file_item_find_child(item, menuTag_Name);
+    if (item == NULL) /* no Name tag? */
+        return NULL;
+    item = fm_xml_file_item_find_child(item, FM_XML_FILE_TEXT);
+    if (item == NULL) /* empty Name tag? */
+        return NULL;
+    return fm_xml_file_item_get_data(item, NULL);
+}
+
+static FmXmlFileItem *_find_in_children(GList *list, const char *path)
+{
+    const char *ptr;
+    char *_ptr;
+
+    if (list == NULL)
+        return NULL;
+    g_debug("menu tree: searching for '%s'", path);
+    ptr = strchr(path, '/');
+    if (ptr == NULL)
+    {
+        ptr = path;
+        path = _ptr = NULL;
+    }
+    else
+    {
+        _ptr = g_strndup(path, ptr - path);
+        path = ptr + 1;
+        ptr = _ptr;
+    }
+    while (list)
+    {
+        const char *elem_name = _get_menu_name(list->data);
+        /* g_debug("got child %d: %s", fm_xml_file_item_get_tag(list->data), elem_name); */
+        if (g_strcmp0(elem_name, ptr) == 0)
+            break;
+        else
+            list = list->next;
+    }
+    g_free(_ptr);
+    if (list && path)
+    {
+        FmXmlFileItem *item;
+
+        list = fm_xml_file_item_get_children(list->data);
+        item = _find_in_children(list, path);
+        g_list_free(list);
+        return item;
+    }
+    return list ? list->data : NULL;
+}
+
+/* returns only <Menu> child */
+static FmXmlFileItem *_set_default_contents(FmXmlFile *file, const char *basename)
+{
+    FmXmlFileItem *item, *child;
+    char *path;
+
+    /* set DTD */
+    fm_xml_file_set_dtd(file,
+                        "Menu PUBLIC '-//freedesktop//DTD Menu 1.0//EN'\n"
+                        " 'http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd'",
+                        NULL);
+    /* set content:
+        <Menu>
+            <Name>Applications</Name>
+            <MergeFile type='parent'>/etc/xgd/menus/%s</MergeFile>
+        </Menu> */
+    item = fm_xml_file_item_new(menuTag_Menu);
+    fm_xml_file_insert_first(file, item);
+    child = fm_xml_file_item_new(menuTag_Name);
+    fm_xml_file_item_append_text(child, "Applications", -1, FALSE);
+    fm_xml_file_item_append_child(item, child);
+    child = fm_xml_file_item_new(menuTag_MergeFile);
+    fm_xml_file_item_set_attribute(child, "type", "parent");
+    /* FIXME: what is correct way to handle this? is it required at all? */
+    path = g_strdup_printf("/etc/xgd/menus/%s", basename);
+    fm_xml_file_item_append_text(child, path, -1, FALSE);
+    g_free(path);
+    fm_xml_file_item_append_child(item, child);
+    return item;
+    /* FIXME: can errors happen above? */
+}
+
+/* tries to create <Menu>... path in children list and returns <Menu> for it */
+static FmXmlFileItem *_create_path_in_tree(FmXmlFileItem *parent, const char *path)
+{
+    const char *ptr;
+    char *_ptr;
+    GList *list, *l;
+    FmXmlFileItem *item, *name;
+
+    if (path == NULL)
+        return NULL;
+    list = fm_xml_file_item_get_children(parent);
+    /* g_debug("menu tree: creating '%s'", path); */
+    ptr = strchr(path, '/');
+    if (ptr == NULL)
+    {
+        ptr = path;
+        path = _ptr = NULL;
+    }
+    else
+    {
+        _ptr = g_strndup(path, ptr - path);
+        path = ptr + 1;
+        ptr = _ptr;
+    }
+    for (l = list; l; l = l->next)
+    {
+        const char *elem_name = _get_menu_name(l->data);
+        /* g_debug("got child %d: %s", fm_xml_file_item_get_tag(list->data), elem_name); */
+        if (g_strcmp0(elem_name, ptr) == 0)
+            break;
+    }
+    if (l) /* subpath already exists */
+    {
+        item = l->data;
+        g_list_free(list);
+        g_free(_ptr);
+        return _create_path_in_tree(item, path);
+    }
+    g_list_free(list);
+    /* create subtag <Name> */
+    name = fm_xml_file_item_new(menuTag_Name);
+    fm_xml_file_item_append_text(name, ptr, -1, FALSE);
+    g_free(_ptr);
+    /* create <Menu> and insert it */
+    item = fm_xml_file_item_new(menuTag_Menu);
+    if (!fm_xml_file_item_append_child(parent, item) ||
+        !fm_xml_file_item_append_child(item, name))
+    {
+        /* FIXME: it cannot fail on newly created items! */
+        fm_xml_file_item_destroy(name);
+        fm_xml_file_item_destroy(item);
+        return NULL;
+    }
+    /* path is NULL if it is a final subpath */
+    return path ? _create_path_in_tree(item, path) : item;
+}
+
+/* locks menuTree, sets fields in data, sets gf
+   returns "Applications" menu on success and NULL on failure */
+static FmXmlFileItem *_prepare_contents(FmMenuMenuTree *data, GCancellable *cancellable,
+                                        GError **error, GFile **gf)
+{
+    const char *xdg_menu_prefix;
+    char *contents;
+    gsize len;
+    GList *xml = NULL;
+    FmXmlFileItem *apps;
+    gboolean ok;
+
+    /* do it in compatibility with lxpanel */
+    xdg_menu_prefix = g_getenv("XDG_MENU_PREFIX");
+    contents = g_strdup_printf("%sapplications.menu",
+                               xdg_menu_prefix ? xdg_menu_prefix : "lxde-");
+    data->file_path = g_build_filename(g_get_user_config_dir(), "menus",
+                                       contents, NULL);
+    *gf = g_file_new_for_path(data->file_path);
+    data->menu = fm_xml_file_new(NULL);
+    data->line = data->pos = -1;
+    data->cancellable = cancellable;
+    G_LOCK(menuTree);
+    /* set tags, ignore errors */
+    menuTag_Menu = fm_xml_file_set_handler(data->menu, "Menu",
+                                           &_menu_xml_handler_pass, FALSE, NULL);
+    menuTag_Name = fm_xml_file_set_handler(data->menu, "Name",
+                                           &_menu_xml_handler_pass, FALSE, NULL);
+    menuTag_Deleted = fm_xml_file_set_handler(data->menu, "Deleted",
+                                              &_menu_xml_handler_pass, FALSE, NULL);
+    menuTag_NotDeleted = fm_xml_file_set_handler(data->menu, "NotDeleted",
+                                                 &_menu_xml_handler_pass, FALSE, NULL);
+    menuTag_Directory = fm_xml_file_set_handler(data->menu, "Directory",
+                                                &_menu_xml_handler_pass, FALSE, NULL);
+    menuTag_Include = fm_xml_file_set_handler(data->menu, "Include",
+                                              &_menu_xml_handler_pass, FALSE, NULL);
+    menuTag_Exclude = fm_xml_file_set_handler(data->menu, "Exclude",
+                                              &_menu_xml_handler_pass, FALSE, NULL);
+    menuTag_Filename = fm_xml_file_set_handler(data->menu, "Filename",
+                                               &_menu_xml_handler_pass, FALSE, NULL);
+    menuTag_MergeFile = fm_xml_file_set_handler(data->menu, "MergeFile",
+                                                &_menu_xml_handler_pass, FALSE, NULL);
+    menuTag_Category = fm_xml_file_set_handler(data->menu, "Category",
+                                               &_menu_xml_handler_pass, FALSE, NULL);
+    if (!g_file_query_exists(*gf, cancellable))
+    {
+        /* if file doesn't exist then it should be created with default contents */
+        apps = _set_default_contents(data->menu, contents);
+        g_free(contents);
+        return apps;
+    }
+    g_free(contents); /* we used it temporarily */
+    contents = NULL;
+    ok = g_file_load_contents(*gf, cancellable, &contents, &len, NULL, error);
+    if (!ok)
+        return NULL;
+    ok = fm_xml_file_parse_data(data->menu, contents, len, error, data);
+    g_free(contents);
+    if (ok)
+        xml = fm_xml_file_finish_parse(data->menu, error);
+    if (xml == NULL) /* error is set by failed function */
+    {
+        if (data->line == -1)
+            data->line = fm_xml_file_get_current_line(data->menu, &data->pos);
+        g_prefix_error(error, _("XML file '%s' error (%d:%d): "), data->file_path,
+                       data->line, data->pos);
+    }
+    else
+    {
+        apps = _find_in_children(xml, "Applications");
+        g_list_free(xml);
+        if (apps)
+            return apps;
+        g_set_error_literal(error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
+                            _("XML file doesn't contain Applications root"));
+    }
+    return NULL;
+}
+
+/* replaces invalid chars in path with minus sign */
+static inline char *_get_pathtag_for_path(const char *path)
+{
+    char *pathtag, *c;
+
+    pathtag = g_strdup(path);
+    for (c = pathtag; *c; c++)
+        if (*c == '/' || *c == '\t' || *c == '\n' || *c == '\r' || *c == ' ')
+            *c = '-';
+    return pathtag;
+}
+
+static gboolean _save_new_menu_file(GFile *gf, FmXmlFile *file,
+                                    GCancellable *cancellable,
+                                    GError **error)
+{
+    gsize len;
+    char *contents = fm_xml_file_to_data(file, &len, error);
+    gboolean result = FALSE;
+
+    if (contents == NULL)
+        return FALSE;
+    /* g_debug("new menu file: %s", contents); */
+    result = g_file_replace_contents(gf, contents, len, NULL, FALSE,
+                                     G_FILE_CREATE_REPLACE_DESTINATION, NULL,
+                                     cancellable, error);
+    g_free(contents);
+    return result;
+}
+
+#if MENU_CACHE_CHECK_VERSION(0, 5, 0)
+/* changes .menu XML file */
+static gboolean _remove_directory(const char *path, GCancellable *cancellable,
+                                  GError **error)
+{
+    GList *xml = NULL, *it;
+    GFile *gf;
+    FmXmlFileItem *apps, *item;
+    FmMenuMenuTree data;
+    gboolean ok = TRUE;
+
+    /* g_debug("deleting menu folder '%s'", path); */
+    /* FIXME: check if there is that path in the XML tree before doing anything */
+    apps = _prepare_contents(&data, cancellable, error, &gf);
+    if (apps == NULL)
+    {
+        /* either failed to load contents or cancelled */
+        ok = FALSE;
+    }
+    else if ((xml = fm_xml_file_item_get_children(apps)) != NULL &&
+             (item = _find_in_children(xml, path)) != NULL)
+    {
+        /* if path is found and has <NotDeleted/> then replace it with <Deleted/> */
+        g_list_free(xml);
+        xml = fm_xml_file_item_get_children(item);
+        for (it = xml; it; it = it->next)
+        {
+            FmXmlFileTag tag = fm_xml_file_item_get_tag(it->data);
+            if (tag == menuTag_Deleted || tag == menuTag_NotDeleted)
+                fm_xml_file_item_destroy(it->data);
+        }
+        goto _add_deleted_tag;
+    }
+    else
+    {
+        /* else create path and add <Deleted/> to it */
+        item = _create_path_in_tree(apps, path);
+        if (item == NULL)
+        {
+            g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+                        _("Cannot create XML definition for '%s'"), path);
+            ok = FALSE;
+        }
+        else
+        {
+            FmXmlFileItem *item2;
+
+_add_deleted_tag:
+            item2 = fm_xml_file_item_new(menuTag_Deleted);
+            fm_xml_file_item_set_comment(item2, "deleted by LibFM");
+            fm_xml_file_item_append_child(item, item2); /* NOTE: it cannot fail */
+        }
+    }
+    if (ok)
+        ok = _save_new_menu_file(gf, data.menu, cancellable, error);
+    G_UNLOCK(menuTree);
+    g_object_unref(gf);
+    g_object_unref(data.menu);
+    g_free(data.file_path);
+    g_list_free(xml);
+    return ok;
+}
+
+/* changes .menu XML file */
+static gboolean _add_directory(const char *path, GCancellable *cancellable,
+                               GError **error)
+{
+    GList *xml = NULL, *it;
+    GFile *gf;
+    FmXmlFileItem *apps, *item, *child;
+    FmMenuMenuTree data;
+    gboolean ok = TRUE;
+
+    /* g_debug("adding menu folder '%s'", path); */
+    /* FIXME: fail if such Menu Name already not deleted in XML tree */
+    apps = _prepare_contents(&data, cancellable, error, &gf);
+    if (apps == NULL)
+    {
+        /* either failed to load contents or cancelled */
+        ok = FALSE;
+    }
+    else if ((xml = fm_xml_file_item_get_children(apps)) != NULL &&
+             (item = _find_in_children(xml, path)) != NULL)
+    {
+        /* "undelete" the directory: */
+        /* if path is found and has <Deleted/> then replace it with <NotDeleted/> */
+        g_list_free(xml);
+        xml = fm_xml_file_item_get_children(item);
+        ok = FALSE; /* it should be Deleted, otherwise error, see FIXME above */
+        for (it = xml; it; it = it->next)
+        {
+            FmXmlFileTag tag = fm_xml_file_item_get_tag(it->data);
+            if (tag == menuTag_Deleted)
+            {
+                fm_xml_file_item_destroy(it->data);
+                ok = TRUE; /* see FIXME above */
+            }
+            else if (tag == menuTag_NotDeleted)
+            {
+                fm_xml_file_item_destroy(it->data);
+                ok = FALSE; /* see FIXME above */
+            }
+        }
+        if (!ok) /* see FIXME above */
+            g_set_error(error, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                        _("Menu path '%s' already exists"), path);
+        else
+        {
+            child = fm_xml_file_item_new(menuTag_NotDeleted);
+            fm_xml_file_item_set_comment(child, "undeleted by LibFM");
+            fm_xml_file_item_append_child(item, child); /* NOTE: it cannot fail */
+        }
+    }
+    else
+    {
+        /* else create path and add content to it */
+        item = _create_path_in_tree(apps, path);
+        if (item == NULL)
+        {
+            g_set_error(error, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                        _("Cannot create XML definition for '%s'"), path);
+            ok = FALSE;
+        }
+        else
+        {
+            char *pathtag, *dir, *contents;
+            GString *str;
+
+            /* add <NotDeleted/> */
+            child = fm_xml_file_item_new(menuTag_NotDeleted);
+            fm_xml_file_item_append_child(item, child);
+            /* touch .directory file, ignore errors */
+            dir = strrchr(path, '/'); /* use it as a storage for basename */
+            if (dir)
+                dir++;
+            else
+                dir = (char *)path;
+            contents = g_strdup_printf("[" G_KEY_FILE_DESKTOP_GROUP "]\n"
+                                       G_KEY_FILE_DESKTOP_KEY_TYPE "=" G_KEY_FILE_DESKTOP_TYPE_DIRECTORY "\n"
+                                       G_KEY_FILE_DESKTOP_KEY_NAME "=%s", dir);
+            pathtag = _get_pathtag_for_path(path);
+            dir = g_build_filename(g_get_user_data_dir(), "desktop-directories",
+                                   pathtag, NULL);
+            str = g_string_new(dir);
+            g_free(dir);
+            g_string_append(str, ".directory");
+            g_file_set_contents(str->str, contents, -1, NULL);
+            /* FIXME: report errors */
+            g_free(contents);
+            /* add <Directory>.....</Directory> */
+            child = fm_xml_file_item_new(menuTag_Directory);
+            g_string_printf(str, "%s.directory", pathtag);
+            fm_xml_file_item_append_text(child, str->str, str->len, FALSE);
+            fm_xml_file_item_append_child(item, child);
+            /* add <Include><Category>......</Category></Include> */
+            child = fm_xml_file_item_new(menuTag_Include);
+            fm_xml_file_item_append_child(item, child);
+            g_string_printf(str, "X-%s", pathtag);
+            g_free(pathtag);
+            item = fm_xml_file_item_new(menuTag_Category); /* reuse item var. */
+            fm_xml_file_item_append_text(item, str->str, str->len, FALSE);
+            fm_xml_file_item_append_child(child, item);
+            g_string_free(str, TRUE);
+            /* ignoring errors since new created items cannot fail on append */
+        }
+    }
+    if (ok)
+        ok = _save_new_menu_file(gf, data.menu, cancellable, error);
+    G_UNLOCK(menuTree);
+    g_object_unref(gf);
+    g_object_unref(data.menu);
+    g_free(data.file_path);
+    g_list_free(xml);
+    return ok;
+}
+#endif
+
+/* changes .menu XML file */
+static gboolean _add_application(const char *path, GCancellable *cancellable,
+                                 GError **error)
+{
+    const char *id;
+    char *dir;
+    GList *xml = NULL, *it;
+    GFile *gf;
+    FmXmlFileItem *apps, *item, *child;
+    FmMenuMenuTree data;
+    gboolean ok = TRUE;
+
+    id = strrchr(path, '/');
+    if (id == NULL)
+    {
+        dir = NULL;
+        id = path;
+    }
+    else
+    {
+        dir = g_strndup(path, id - path);
+        id++;
+    }
+    apps = _prepare_contents(&data, cancellable, error, &gf);
+    if (apps == NULL)
+    {
+        /* either failed to load contents or cancelled */
+        ok = FALSE;
+    }
+    else if (dir == NULL) /* adding to root, use apps as target */
+    {
+        item = apps;
+        goto _set;
+    }
+    else if ((xml = fm_xml_file_item_get_children(apps)) != NULL &&
+             (item = _find_in_children(xml, dir)) != NULL)
+    {
+        /* already found that path */
+        goto _set;
+    }
+    else
+    {
+        /* else create path and add content to it */
+        item = _create_path_in_tree(apps, dir);
+        if (item == NULL)
+        {
+            g_set_error(error, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                        _("Cannot create XML definition for '%s'"), path);
+            ok = FALSE;
+        }
+        else
+        {
+_set:
+            g_list_free(xml);
+            xml = fm_xml_file_item_get_children(item);
+            ok = FALSE; /* check if Include is already there */
+            /* remove <Exclude><Filename>id</Filename></Exclude> */
+            for (it = xml; it; it = it->next)
+            {
+                FmXmlFileTag tag = fm_xml_file_item_get_tag(it->data);
+                if (tag == menuTag_Exclude)
+                {
+                    /* get Filename tag */
+                    child = fm_xml_file_item_find_child(it->data, menuTag_Filename);
+                    if (child == NULL)
+                        continue;
+                    /* get contents of the tag */
+                    child = fm_xml_file_item_find_child(child, FM_XML_FILE_TEXT);
+                    if (child == NULL)
+                        continue;
+                    if (strcmp(fm_xml_file_item_get_data(child, NULL), id) != 0)
+                        continue;
+                    fm_xml_file_item_destroy(it->data);
+                    /* it was excluded before so removing exclude will add it */
+                    ok = TRUE;
+                }
+                else if (!ok && tag == menuTag_Include)
+                {
+                    /* get Filename tag */
+                    child = fm_xml_file_item_find_child(it->data, menuTag_Filename);
+                    if (child == NULL)
+                        continue;
+                    /* get contents of the tag */
+                    child = fm_xml_file_item_find_child(child, FM_XML_FILE_TEXT);
+                    if (child == NULL)
+                        continue;
+                    if (strcmp(fm_xml_file_item_get_data(child, NULL), id) == 0)
+                        ok = TRUE; /* found! */
+                }
+            }
+            if (!ok)
+            {
+                /* add <Include><Filename>id</Filename></Include> */
+                child = fm_xml_file_item_new(menuTag_Include);
+                fm_xml_file_item_set_comment(child, "added by LibFM");
+                fm_xml_file_item_append_child(item, child);
+                item = fm_xml_file_item_new(menuTag_Filename);
+                fm_xml_file_item_append_text(item, id, -1, FALSE);
+                fm_xml_file_item_append_child(child, item);
+                ok = TRUE;
+            }
+        }
+    }
+    if (ok)
+        ok = _save_new_menu_file(gf, data.menu, cancellable, error);
+    G_UNLOCK(menuTree);
+    g_object_unref(gf);
+    g_object_unref(data.menu);
+    g_free(data.file_path);
+    g_list_free(xml);
+    g_free(dir);
+    return ok;
+}
+
+/* changes .menu XML file */
+static gboolean _remove_application(const char *path, GCancellable *cancellable,
+                                    GError **error)
+{
+    const char *id;
+    char *dir;
+    GList *xml = NULL, *it;
+    GFile *gf;
+    FmXmlFileItem *apps, *item, *child;
+    FmMenuMenuTree data;
+    gboolean ok = TRUE;
+
+    id = strrchr(path, '/');
+    if (id == NULL)
+    {
+        dir = NULL;
+        id = path;
+    }
+    else
+    {
+        dir = g_strndup(path, id - path);
+        id++;
+    }
+    apps = _prepare_contents(&data, cancellable, error, &gf);
+    if (apps == NULL)
+    {
+        /* either failed to load contents or cancelled */
+        ok = FALSE;
+    }
+    else if (dir == NULL) /* removing from root, use apps as target */
+    {
+        item = apps;
+        goto _set;
+    }
+    else if ((xml = fm_xml_file_item_get_children(apps)) != NULL &&
+             (item = _find_in_children(xml, dir)) != NULL)
+    {
+        /* already found that path */
+        goto _set;
+    }
+    else
+    {
+        /* else create path and add content to it */
+        item = _create_path_in_tree(apps, dir);
+        if (item == NULL)
+        {
+            g_set_error(error, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                        _("Cannot create XML definition for '%s'"), path);
+            ok = FALSE;
+        }
+        else
+        {
+_set:
+            g_list_free(xml);
+            xml = fm_xml_file_item_get_children(item);
+            ok = FALSE; /* check if Include is already there */
+            /* remove <Include><Filename>id</Filename></Include> */
+            for (it = xml; it; it = it->next)
+            {
+                FmXmlFileTag tag = fm_xml_file_item_get_tag(it->data);
+                if (tag == menuTag_Include)
+                {
+                    /* get Filename tag */
+                    child = fm_xml_file_item_find_child(it->data, menuTag_Filename);
+                    if (child == NULL)
+                        continue;
+                    /* get contents of the tag */
+                    child = fm_xml_file_item_find_child(child, FM_XML_FILE_TEXT);
+                    if (child == NULL)
+                        continue;
+                    if (strcmp(fm_xml_file_item_get_data(child, NULL), id) != 0)
+                        continue;
+                    fm_xml_file_item_destroy(it->data);
+                    /* it was included before so removing include will remove it */
+                    ok = TRUE;
+                }
+                else if (!ok && tag == menuTag_Exclude)
+                {
+                    /* get Filename tag */
+                    child = fm_xml_file_item_find_child(it->data, menuTag_Filename);
+                    if (child == NULL)
+                        continue;
+                    /* get contents of the tag */
+                    child = fm_xml_file_item_find_child(child, FM_XML_FILE_TEXT);
+                    if (child == NULL)
+                        continue;
+                    if (strcmp(fm_xml_file_item_get_data(child, NULL), id) == 0)
+                        ok = TRUE; /* found! */
+                }
+            }
+            if (!ok)
+            {
+                /* add <Exclude><Filename>id</Filename></Exclude> */
+                child = fm_xml_file_item_new(menuTag_Exclude);
+                fm_xml_file_item_set_comment(child, "deleted by LibFM");
+                fm_xml_file_item_append_child(item, child);
+                item = fm_xml_file_item_new(menuTag_Filename);
+                fm_xml_file_item_append_text(item, id, -1, FALSE);
+                fm_xml_file_item_append_child(child, item);
+                ok = TRUE;
+            }
+        }
+    }
+    if (ok)
+        ok = _save_new_menu_file(gf, data.menu, cancellable, error);
+    G_UNLOCK(menuTree);
+    g_object_unref(gf);
+    g_object_unref(data.menu);
+    g_free(data.file_path);
+    g_list_free(xml);
+    g_free(dir);
+    return ok;
+}
+
+
+/* ---- FmMenuVFile class ---- */
+#define FM_TYPE_MENU_VFILE             (fm_vfs_menu_file_get_type())
+#define FM_MENU_VFILE(o)               (G_TYPE_CHECK_INSTANCE_CAST((o), \
+                                        FM_TYPE_MENU_VFILE, FmMenuVFile))
+
+typedef struct _FmMenuVFile             FmMenuVFile;
+typedef struct _FmMenuVFileClass        FmMenuVFileClass;
+
+static GType fm_vfs_menu_file_get_type  (void);
+
+struct _FmMenuVFile
+{
+    GObject parent_object;
+
+    char *path;
+};
+
+struct _FmMenuVFileClass
+{
+  GObjectClass parent_class;
+};
+
+static void fm_menu_g_file_init(GFileIface *iface);
+static void fm_menu_fm_file_init(FmFileInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE(FmMenuVFile, fm_vfs_menu_file, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE(G_TYPE_FILE, fm_menu_g_file_init)
+                        G_IMPLEMENT_INTERFACE(FM_TYPE_FILE, fm_menu_fm_file_init))
+
+static void fm_vfs_menu_file_finalize(GObject *object)
+{
+    FmMenuVFile *item = FM_MENU_VFILE(object);
+
+    g_free(item->path);
+
+    G_OBJECT_CLASS(fm_vfs_menu_file_parent_class)->finalize(object);
+}
+
+static void fm_vfs_menu_file_class_init(FmMenuVFileClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+    gobject_class->finalize = fm_vfs_menu_file_finalize;
+}
+
+static void fm_vfs_menu_file_init(FmMenuVFile *item)
+{
+    /* nothing */
+}
+
+static FmMenuVFile *_fm_menu_vfile_new(void)
+{
+    return (FmMenuVFile*)g_object_new(FM_TYPE_MENU_VFILE, NULL);
+}
+
+
+/* ---- menu enumerator class ---- */
+#define FM_TYPE_VFS_MENU_ENUMERATOR        (fm_vfs_menu_enumerator_get_type())
+#define FM_VFS_MENU_ENUMERATOR(o)          (G_TYPE_CHECK_INSTANCE_CAST((o), \
+                            FM_TYPE_VFS_MENU_ENUMERATOR, FmVfsMenuEnumerator))
+
+typedef struct _FmVfsMenuEnumerator         FmVfsMenuEnumerator;
+typedef struct _FmVfsMenuEnumeratorClass    FmVfsMenuEnumeratorClass;
+
+struct _FmVfsMenuEnumerator
+{
+    GFileEnumerator parent;
+
+    MenuCache *mc;
+    GSList *child;
+    guint32 de_flag;
+};
+
+struct _FmVfsMenuEnumeratorClass
+{
+    GFileEnumeratorClass parent_class;
+};
+
+static GType fm_vfs_menu_enumerator_get_type   (void);
+
+G_DEFINE_TYPE(FmVfsMenuEnumerator, fm_vfs_menu_enumerator, G_TYPE_FILE_ENUMERATOR)
+
+static void _fm_vfs_menu_enumerator_dispose(GObject *object)
+{
+    FmVfsMenuEnumerator *enu = FM_VFS_MENU_ENUMERATOR(object);
+
+    if(enu->mc)
+    {
+        menu_cache_unref(enu->mc);
+        enu->mc = NULL;
+    }
+
+    G_OBJECT_CLASS(fm_vfs_menu_enumerator_parent_class)->dispose(object);
+}
+
+GIcon* _fm_icon_from_name(const char* name); /* defined in core/iconinfo.cpp */
+
+static GFileInfo *_g_file_info_from_menu_cache_item(MenuCacheItem *item,
+                                                    /* GFileAttributeMatcher *attribute_matcher, */
+                                                    guint32 de_flag)
+{
+    GFileInfo *fileinfo = g_file_info_new();
+    const char *icon_name;
+    GIcon* icon;
+
+    /* FIXME: use g_uri_escape_string() for item name */
+    g_file_info_set_name(fileinfo, menu_cache_item_get_id(item));
+    if(menu_cache_item_get_name(item) != NULL)
+        g_file_info_set_display_name(fileinfo, menu_cache_item_get_name(item));
+
+    /* the setup below was in fm_file_info_set_from_menu_cache_item()
+       so this setup makes latter API deprecated */
+    icon_name = menu_cache_item_get_icon(item);
+    if(icon_name)
+    {
+        icon = _fm_icon_from_name(icon_name);
+        if(G_LIKELY(icon))
+        {
+            g_file_info_set_icon(fileinfo, icon);
+            g_object_unref(icon);
+        }
+    }
+    if(menu_cache_item_get_type(item) == MENU_CACHE_TYPE_DIR)
+    {
+        g_file_info_set_file_type(fileinfo, G_FILE_TYPE_DIRECTORY);
+#if MENU_CACHE_CHECK_VERSION(0, 5, 0)
+        g_file_info_set_is_hidden(fileinfo,
+                                  !menu_cache_dir_is_visible(MENU_CACHE_DIR(item)));
+#else
+        g_file_info_set_is_hidden(fileinfo, FALSE);
+#endif
+    }
+    else /* MENU_CACHE_TYPE_APP */
+    {
+        char *path = menu_cache_item_get_file_path(item);
+        g_file_info_set_file_type(fileinfo, G_FILE_TYPE_SHORTCUT);
+        g_file_info_set_attribute_string(fileinfo,
+                                         G_FILE_ATTRIBUTE_STANDARD_TARGET_URI,
+                                         path);
+        g_free(path);
+        g_file_info_set_content_type(fileinfo, "application/x-desktop");
+        g_file_info_set_is_hidden(fileinfo,
+                                  !menu_cache_app_get_is_visible(MENU_CACHE_APP(item),
+                                                                 de_flag));
+    }
+// FIXME: use attribute_matcher and set G_FILE_ATTRIBUTE_ACCESS_CAN_{WRITE,READ,EXECUTE,DELETE}
+    g_file_info_set_attribute_string(fileinfo, G_FILE_ATTRIBUTE_ID_FILESYSTEM,
+                                     "menu-Applications");
+    g_file_info_set_attribute_boolean(fileinfo,
+                                      G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, TRUE);
+    g_file_info_set_attribute_boolean(fileinfo,
+                                      G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
+    return fileinfo;
+}
+
+typedef struct
+{
+    union
+    {
+        FmVfsMenuEnumerator *enumerator;
+        const char *path_str;
+    };
+    union
+    {
+        FmMenuVFile *destination;
+//        const char *attributes;
+        const char *display_name;
+        GFileInfo *info;
+    };
+//    GFileQueryInfoFlags flags;
+    union
+    {
+        GCancellable *cancellable;
+        GFile *file;
+    };
+    GError **error;
+    gpointer result;
+} FmVfsMenuMainThreadData;
+
+static gboolean _fm_vfs_menu_enumerator_next_file_real(gpointer data)
+{
+    FmVfsMenuMainThreadData *init = data;
+    FmVfsMenuEnumerator *enu = init->enumerator;
+    GSList *child = enu->child;
+    MenuCacheItem *item;
+
+    init->result = NULL;
+
+    if(child == NULL)
+        goto done;
+
+    for(; child; child = child->next)
+    {
+        if(g_cancellable_set_error_if_cancelled(init->cancellable, init->error))
+            break;
+        item = MENU_CACHE_ITEM(child->data);
+        if(!item || menu_cache_item_get_type(item) == MENU_CACHE_TYPE_SEP ||
+           menu_cache_item_get_type(item) == MENU_CACHE_TYPE_NONE)
+            continue;
+#if 0
+        /* also hide menu items which should be hidden in current DE. */
+        if(menu_cache_item_get_type(item) == MENU_CACHE_TYPE_APP
+           && !menu_cache_app_get_is_visible(MENU_CACHE_APP(item), enu->de_flag))
+            continue;
+#endif
+
+        init->result = _g_file_info_from_menu_cache_item(item, enu->de_flag);
+        child = child->next;
+        break;
+    }
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    while(enu->child != child) /* free skipped/used elements */
+    {
+        GSList *ch = enu->child;
+        enu->child = ch->next;
+        menu_cache_item_unref(ch->data);
+        g_slist_free_1(ch);
+    }
+#else
+    enu->child = child;
+#endif
+
+done:
+    return FALSE;
+}
+
+static GFileInfo *_fm_vfs_menu_enumerator_next_file(GFileEnumerator *enumerator,
+                                                    GCancellable *cancellable,
+                                                    GError **error)
+{
+    FmVfsMenuMainThreadData init;
+
+    init.enumerator = FM_VFS_MENU_ENUMERATOR(enumerator);
+    init.cancellable = cancellable;
+    init.error = error;
+    RUN_WITH_MENU_CACHE(_fm_vfs_menu_enumerator_next_file_real, &init);
+    return init.result;
+}
+
+static gboolean _fm_vfs_menu_enumerator_close(GFileEnumerator *enumerator,
+                                              GCancellable *cancellable,
+                                              GError **error)
+{
+    FmVfsMenuEnumerator *enu = FM_VFS_MENU_ENUMERATOR(enumerator);
+
+    if(enu->mc)
+    {
+        menu_cache_unref(enu->mc);
+        enu->mc = NULL;
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+        g_slist_free_full(enu->child, (GDestroyNotify)menu_cache_item_unref);
+#endif
+        enu->child = NULL;
+    }
+    return TRUE;
+}
+
+static void fm_vfs_menu_enumerator_class_init(FmVfsMenuEnumeratorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+  GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS(klass);
+
+  gobject_class->dispose = _fm_vfs_menu_enumerator_dispose;
+
+  enumerator_class->next_file = _fm_vfs_menu_enumerator_next_file;
+  enumerator_class->close_fn = _fm_vfs_menu_enumerator_close;
+}
+
+static void fm_vfs_menu_enumerator_init(FmVfsMenuEnumerator *enumerator)
+{
+    /* nothing */
+}
+
+static MenuCacheItem *_vfile_path_to_menu_cache_item(MenuCache* mc, const char *path)
+{
+    MenuCacheItem *dir;
+    char *unescaped, *tmp = NULL;
+
+    unescaped = g_uri_unescape_string(path, NULL);
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    dir = MENU_CACHE_ITEM(menu_cache_dup_root_dir(mc));
+#else
+    dir = MENU_CACHE_ITEM(menu_cache_get_root_dir(mc));
+#endif
+    if(dir)
+    {
+#if !MENU_CACHE_CHECK_VERSION(0, 5, 0)
+        char *id;
+#if !MENU_CACHE_CHECK_VERSION(0, 4, 0)
+        GSList *child;
+#endif
+#endif
+        tmp = g_strconcat("/", menu_cache_item_get_id(dir), "/", unescaped, NULL);
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+        menu_cache_item_unref(dir);
+        dir = menu_cache_item_from_path(mc, tmp);
+#else
+        /* access not dir is a bit tricky */
+        id = strrchr(tmp, '/');
+        *id++ = '\0';
+        dir = MENU_CACHE_ITEM(menu_cache_get_dir_from_path(mc, tmp));
+        child = menu_cache_dir_get_children(MENU_CACHE_DIR(dir));
+        dir = NULL;
+        while (child)
+        {
+            if (g_strcmp0(id, menu_cache_item_get_id(child->data)) == 0)
+            {
+                dir = child->data;
+                break;
+            }
+            child = child->next;
+        }
+#endif
+#if !MENU_CACHE_CHECK_VERSION(0, 5, 0)
+        /* The menu-cache is buggy and returns parent for invalid path
+           instead of failure so we check what we got here.
+           Unfortunately we cannot detect if requested name is the same
+           as its parent and menu-cache returned the parent. */
+        id = strrchr(unescaped, '/');
+        if(id)
+            id++;
+        else
+            id = unescaped;
+        if(dir != NULL && strcmp(id, menu_cache_item_get_id(dir)) != 0)
+            dir = NULL;
+#endif
+    }
+    g_free(unescaped);
+    g_free(tmp);
+    /* NOTE: returned value is referenced for >= 0.4.0 only */
+    return dir;
+}
+
+static MenuCache *_get_menu_cache(GError **error)
+{
+    MenuCache *mc;
+    static gboolean environment_tested = FALSE;
+    static gboolean requires_prefix = FALSE;
+
+    /* do it in compatibility with lxpanel */
+    if(!environment_tested)
+    {
+        requires_prefix = (g_getenv("XDG_MENU_PREFIX") == NULL);
+        environment_tested = TRUE;
+    }
+#if MENU_CACHE_CHECK_VERSION(0, 5, 0)
+    mc = menu_cache_lookup_sync(requires_prefix ? "lxde-applications.menu+hidden" : "applications.menu+hidden");
+#else
+    mc = menu_cache_lookup_sync(requires_prefix ? "lxde-applications.menu" : "applications.menu");
+#endif
+    /* FIXME: may be it is reasonable to set XDG_MENU_PREFIX ? */
+
+    if(mc == NULL) /* initialization failed */
+        g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                            _("Menu cache error"));
+    return mc;
+}
+
+static gboolean _fm_vfs_menu_enumerator_new_real(gpointer data)
+{
+    FmVfsMenuMainThreadData *init = data;
+    FmVfsMenuEnumerator *enumerator;
+    MenuCache* mc;
+    const char *de_name;
+    MenuCacheItem *dir;
+
+    mc = _get_menu_cache(init->error);
+
+    if(mc == NULL) /* initialization failed */
+        return FALSE;
+
+    enumerator = g_object_new(FM_TYPE_VFS_MENU_ENUMERATOR, "container",
+                              init->file, NULL);
+    enumerator->mc = mc;
+    de_name = g_getenv("XDG_CURRENT_DESKTOP");
+
+    if(de_name)
+        enumerator->de_flag = menu_cache_get_desktop_env_flag(mc, de_name);
+    else
+        enumerator->de_flag = (guint32)-1;
+
+    /* the menu should be loaded now */
+    if(init->path_str)
+        dir = _vfile_path_to_menu_cache_item(mc, init->path_str);
+    else
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+        dir = MENU_CACHE_ITEM(menu_cache_dup_root_dir(mc));
+#else
+        dir = MENU_CACHE_ITEM(menu_cache_get_root_dir(mc));
+#endif
+    if(dir)
+    {
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+        enumerator->child = menu_cache_dir_list_children(MENU_CACHE_DIR(dir));
+        menu_cache_item_unref(dir);
+#else
+        enumerator->child = menu_cache_dir_get_children(MENU_CACHE_DIR(dir));
+#endif
+    }
+    /* FIXME: do something with attributes and flags */
+
+    init->result = enumerator;
+    return FALSE;
+}
+
+static GFileEnumerator *_fm_vfs_menu_enumerator_new(GFile *file,
+                                                    const char *path_str,
+                                                    const char *attributes,
+                                                    GFileQueryInfoFlags flags,
+                                                    GError **error)
+{
+    FmVfsMenuMainThreadData enu;
+
+    enu.path_str = path_str;
+//    enu.attributes = attributes;
+//    enu.flags = flags;
+    enu.file = file;
+    enu.error = error;
+    enu.result = NULL;
+    RUN_WITH_MENU_CACHE(_fm_vfs_menu_enumerator_new_real, &enu);
+    return enu.result;
+}
+
+
+/* ---- GFile implementation ---- */
+static GFileAttributeInfoList *_fm_vfs_menu_settable_attributes = NULL;
+
+#define ERROR_UNSUPPORTED(err) g_set_error_literal(err, G_IO_ERROR, \
+                        G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"))
+
+static GFile *_fm_vfs_menu_dup(GFile *file)
+{
+    FmMenuVFile *item, *new_item;
+
+    item = FM_MENU_VFILE(file);
+    new_item = _fm_menu_vfile_new();
+    if(item->path)
+        new_item->path = g_strdup(item->path);
+    return (GFile*)new_item;
+}
+
+static guint _fm_vfs_menu_hash(GFile *file)
+{
+    return g_str_hash(FM_MENU_VFILE(file)->path ? FM_MENU_VFILE(file)->path : "/");
+}
+
+static gboolean _fm_vfs_menu_equal(GFile *file1, GFile *file2)
+{
+    char *path1 = FM_MENU_VFILE(file1)->path;
+    char *path2 = FM_MENU_VFILE(file2)->path;
+
+    return g_strcmp0(path1, path2) == 0;
+}
+
+static gboolean _fm_vfs_menu_is_native(GFile *file)
+{
+    return FALSE;
+}
+
+static gboolean _fm_vfs_menu_has_uri_scheme(GFile *file, const char *uri_scheme)
+{
+    return g_ascii_strcasecmp(uri_scheme, "menu") == 0;
+}
+
+static char *_fm_vfs_menu_get_uri_scheme(GFile *file)
+{
+    return g_strdup("menu");
+}
+
+static char *_fm_vfs_menu_get_basename(GFile *file)
+{
+    /* g_debug("_fm_vfs_menu_get_basename %s", FM_MENU_VFILE(file)->path); */
+    if(FM_MENU_VFILE(file)->path == NULL)
+        return g_strdup("/");
+    return g_path_get_basename(FM_MENU_VFILE(file)->path);
+}
+
+static char *_fm_vfs_menu_get_path(GFile *file)
+{
+    return NULL;
+}
+
+static char *_fm_vfs_menu_get_uri(GFile *file)
+{
+    return g_strconcat("menu://applications/", FM_MENU_VFILE(file)->path, NULL);
+}
+
+static char *_fm_vfs_menu_get_parse_name(GFile *file)
+{
+    char *unescaped, *path;
+
+    /* g_debug("_fm_vfs_menu_get_parse_name %s", FM_MENU_VFILE(file)->path); */
+    unescaped = g_uri_unescape_string(FM_MENU_VFILE(file)->path, NULL);
+    path = g_strconcat("menu://applications/", unescaped, NULL);
+    g_free(unescaped);
+    return path;
+}
+
+static GFile *_fm_vfs_menu_get_parent(GFile *file)
+{
+    char *path = FM_MENU_VFILE(file)->path;
+    char *dirname;
+    GFile *parent;
+
+    /* g_debug("_fm_vfs_menu_get_parent %s", path); */
+    if(path)
+    {
+        dirname = g_path_get_dirname(path);
+        if(strcmp(dirname, ".") == 0)
+        {
+            g_free(dirname);
+            path = NULL;
+        }
+        else
+            path = dirname;
+    }
+    parent = _fm_vfs_menu_new_for_uri(path);
+    if(path)
+        g_free(path);
+    return parent;
+}
+
+/* this function is taken from GLocalFile implementation */
+static const char *match_prefix (const char *path, const char *prefix)
+{
+  int prefix_len;
+
+  prefix_len = strlen (prefix);
+  if (strncmp (path, prefix, prefix_len) != 0)
+    return NULL;
+
+  if (prefix_len > 0 && (prefix[prefix_len-1]) == '/')
+    prefix_len--;
+
+  return path + prefix_len;
+}
+
+static gboolean _fm_vfs_menu_prefix_matches(GFile *prefix, GFile *file)
+{
+    const char *path = FM_MENU_VFILE(file)->path;
+    const char *pp = FM_MENU_VFILE(prefix)->path;
+    const char *remainder;
+
+    if(pp == NULL)
+        return TRUE;
+    if(path == NULL)
+        return FALSE;
+    remainder = match_prefix(path, pp);
+    if(remainder != NULL && *remainder == '/')
+        return TRUE;
+    return FALSE;
+}
+
+static char *_fm_vfs_menu_get_relative_path(GFile *parent, GFile *descendant)
+{
+    const char *path = FM_MENU_VFILE(descendant)->path;
+    const char *pp = FM_MENU_VFILE(parent)->path;
+    const char *remainder;
+
+    if(pp == NULL)
+        return g_strdup(path);
+    if(path == NULL)
+        return NULL;
+    remainder = match_prefix(path, pp);
+    if(remainder != NULL && *remainder == '/')
+        return g_uri_unescape_string(&remainder[1], NULL);
+    return NULL;
+}
+
+static GFile *_fm_vfs_menu_resolve_relative_path(GFile *file, const char *relative_path)
+{
+    const char *path = FM_MENU_VFILE(file)->path;
+    FmMenuVFile *new_item = _fm_menu_vfile_new();
+
+    /* g_debug("_fm_vfs_menu_resolve_relative_path %s %s", path, relative_path); */
+    /* FIXME: handle if relative_path is invalid */
+    if(relative_path == NULL || *relative_path == '\0')
+        new_item->path = g_strdup(path);
+    else if(path == NULL)
+        new_item->path = g_strdup(relative_path);
+    else
+    {
+        /* relative_path is the most probably unescaped string (at least GFVS
+           works such way) so we have to escape invalid chars here. */
+        char *escaped = g_uri_escape_string(relative_path,
+                                            G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
+                                            TRUE);
+        new_item->path = g_strconcat(path, "/", relative_path, NULL);
+        g_free(escaped);
+    }
+    return (GFile*)new_item;
+}
+
+static gboolean _fm_vfs_menu_get_child_for_display_name_real(gpointer data)
+{
+    FmVfsMenuMainThreadData *init = data;
+    MenuCache *mc;
+    MenuCacheItem *dir;
+    gboolean is_invalid = FALSE;
+
+    init->result = NULL;
+    mc = _get_menu_cache(init->error);
+    if(mc == NULL)
+        goto _mc_failed;
+
+    if(init->path_str)
+    {
+        dir = _vfile_path_to_menu_cache_item(mc, init->path_str);
+        if(dir == NULL || menu_cache_item_get_type(dir) != MENU_CACHE_TYPE_DIR)
+            is_invalid = TRUE;
+    }
+    else
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+        dir = MENU_CACHE_ITEM(menu_cache_dup_root_dir(mc));
+#else
+        dir = MENU_CACHE_ITEM(menu_cache_get_root_dir(mc));
+#endif
+    if(is_invalid)
+        g_set_error_literal(init->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                            _("Invalid menu directory"));
+    else if(dir)
+    {
+#if MENU_CACHE_CHECK_VERSION(0, 5, 0)
+        MenuCacheItem *item = menu_cache_find_child_by_name(MENU_CACHE_DIR(dir),
+                                                            init->display_name);
+        g_debug("searched for child '%s' found '%s'", init->display_name,
+                item ? menu_cache_item_get_id(item) : "(nil)");
+        if (item == NULL)
+            init->result = _fm_vfs_menu_resolve_relative_path(init->file,
+                                                              init->display_name);
+        else
+        {
+            init->result = _fm_vfs_menu_resolve_relative_path(init->file,
+                                                menu_cache_item_get_id(item));
+            menu_cache_item_unref(item);
+        }
+#else /* < 0.5.0 */
+        GSList *l;
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+        GSList *children = menu_cache_dir_list_children(MENU_CACHE_DIR(dir));
+#else
+        GSList *children = menu_cache_dir_get_children(MENU_CACHE_DIR(dir));
+#endif
+        for (l = children; l; l = l->next)
+            if (g_strcmp0(init->display_name, menu_cache_item_get_name(l->data)) == 0)
+                break;
+        if (l == NULL) /* not found */
+            init->result = _fm_vfs_menu_resolve_relative_path(init->file,
+                                                              init->display_name);
+        else
+            init->result = _fm_vfs_menu_resolve_relative_path(init->file,
+                                                menu_cache_item_get_id(l->data));
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+        g_slist_free_full(children, (GDestroyNotify)menu_cache_item_unref);
+#endif
+#endif /* < 0.5.0 */
+    }
+    else /* menu_cache_get_root_dir failed */
+        g_set_error_literal(init->error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                            _("Menu cache error"));
+
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    if(dir)
+        menu_cache_item_unref(dir);
+#endif
+    menu_cache_unref(mc);
+
+_mc_failed:
+    return FALSE;
+}
+
+static GFile *_fm_vfs_menu_get_child_for_display_name(GFile *file,
+                                                      const char *display_name,
+                                                      GError **error)
+{
+    FmMenuVFile *item = FM_MENU_VFILE(file);
+    FmVfsMenuMainThreadData enu;
+
+    /* g_debug("_fm_vfs_menu_get_child_for_display_name: '%s' '%s'", item->path, display_name); */
+    if (display_name == NULL || *display_name == '\0')
+    {
+        g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                            _("Menu item name cannot be empty"));
+        return NULL;
+    }
+    /* NOTE: this violates GFile requirement that this API should not do I/O but
+       there is no way to do this without dirty tricks which may lead to failure */
+    enu.path_str = item->path;
+    enu.error = error;
+    enu.display_name = display_name;
+    enu.file = file;
+    RUN_WITH_MENU_CACHE(_fm_vfs_menu_get_child_for_display_name_real, &enu);
+    return enu.result;
+}
+
+static GFileEnumerator *_fm_vfs_menu_enumerate_children(GFile *file,
+                                                        const char *attributes,
+                                                        GFileQueryInfoFlags flags,
+                                                        GCancellable *cancellable,
+                                                        GError **error)
+{
+    const char *path = FM_MENU_VFILE(file)->path;
+
+    return _fm_vfs_menu_enumerator_new(file, path, attributes, flags, error);
+}
+
+static gboolean _fm_vfs_menu_query_info_real(gpointer data)
+{
+    FmVfsMenuMainThreadData *init = data;
+    MenuCache *mc;
+    MenuCacheItem *dir;
+    gboolean is_invalid = FALSE;
+
+    init->result = NULL;
+    mc = _get_menu_cache(init->error);
+    if(mc == NULL)
+        goto _mc_failed;
+
+    if(init->path_str)
+    {
+        dir = _vfile_path_to_menu_cache_item(mc, init->path_str);
+        if(dir == NULL)
+            is_invalid = TRUE;
+    }
+    else
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+        dir = MENU_CACHE_ITEM(menu_cache_dup_root_dir(mc));
+#else
+        dir = MENU_CACHE_ITEM(menu_cache_get_root_dir(mc));
+#endif
+    if(is_invalid)
+        g_set_error(init->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                    _("Invalid menu directory '%s'"), init->path_str);
+    else if(dir)
+    {
+        const char *de_name = g_getenv("XDG_CURRENT_DESKTOP");
+
+        if(de_name)
+            init->result = _g_file_info_from_menu_cache_item(dir,
+                                menu_cache_get_desktop_env_flag(mc, de_name));
+        else
+            init->result = _g_file_info_from_menu_cache_item(dir, (guint32)-1);
+    }
+    else /* menu_cache_get_root_dir failed */
+        g_set_error_literal(init->error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                            _("Menu cache error"));
+
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    if(dir)
+        menu_cache_item_unref(dir);
+#endif
+    menu_cache_unref(mc);
+
+_mc_failed:
+    return FALSE;
+}
+
+static GFileInfo *_fm_vfs_menu_query_info(GFile *file,
+                                          const char *attributes,
+                                          GFileQueryInfoFlags flags,
+                                          GCancellable *cancellable,
+                                          GError **error)
+{
+    FmMenuVFile *item = FM_MENU_VFILE(file);
+    GFileInfo *info;
+    GFileAttributeMatcher *matcher;
+    char *basename, *id;
+    FmVfsMenuMainThreadData enu;
+
+    matcher = g_file_attribute_matcher_new(attributes);
+
+    if(item->path == NULL) /* menu root */
+    {
+        info = g_file_info_new();
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_STANDARD_NAME))
+            g_file_info_set_name(info, "/");
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_ID_FILESYSTEM))
+            g_file_info_set_attribute_string(info, G_FILE_ATTRIBUTE_ID_FILESYSTEM,
+                                             "menu-Applications");
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_STANDARD_TYPE))
+            g_file_info_set_file_type(info, G_FILE_TYPE_DIRECTORY);
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_STANDARD_ICON))
+        {
+            GIcon *icon = g_themed_icon_new("system-software-install");
+            g_file_info_set_icon(info, icon);
+            g_object_unref(icon);
+        }
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN))
+            g_file_info_set_is_hidden(info, FALSE);
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))
+            g_file_info_set_display_name(info, _("Applications"));
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME))
+            g_file_info_set_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, FALSE);
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH))
+            g_file_info_set_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
+#if 0
+        /* FIXME: is this ever needed? */
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
+            g_file_info_set_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, TRUE);
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_READ))
+            g_file_info_set_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
+            g_file_info_set_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, FALSE);
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE))
+            g_file_info_set_attribute_boolean(info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
+#endif
+    }
+    else if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_STANDARD_TYPE) ||
+            g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_STANDARD_ICON) ||
+            g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI) ||
+            g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE) ||
+            g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN) ||
+            g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME))
+    {
+        /* retrieve matching attributes from menu-cache */
+        enu.path_str = item->path;
+//        enu.attributes = attributes;
+//        enu.flags = flags;
+        enu.cancellable = cancellable;
+        enu.error = error;
+        RUN_WITH_MENU_CACHE(_fm_vfs_menu_query_info_real, &enu);
+        info = enu.result;
+    }
+    else
+    {
+        info = g_file_info_new();
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_STANDARD_NAME))
+        {
+            basename = g_path_get_basename(item->path);
+            id = g_uri_unescape_string(basename, NULL);
+            g_free(basename);
+            g_file_info_set_name(info, id);
+            g_free(id);
+        }
+        if(g_file_attribute_matcher_matches(matcher, G_FILE_ATTRIBUTE_ID_FILESYSTEM))
+            g_file_info_set_attribute_string(info, G_FILE_ATTRIBUTE_ID_FILESYSTEM,
+                                             "menu-Applications");
+    }
+
+    g_file_attribute_matcher_unref(matcher);
+
+    return info;
+}
+
+static GFileInfo *_fm_vfs_menu_query_filesystem_info(GFile *file,
+                                                     const char *attributes,
+                                                     GCancellable *cancellable,
+                                                     GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static GMount *_fm_vfs_menu_find_enclosing_mount(GFile *file,
+                                                 GCancellable *cancellable,
+                                                 GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static gboolean _fm_vfs_menu_set_display_name_real(gpointer data)
+{
+    FmVfsMenuMainThreadData *init = data;
+    MenuCache *mc;
+    MenuCacheItem *dir;
+    gboolean ok = FALSE;
+
+    mc = _get_menu_cache(init->error);
+    if(mc == NULL)
+        goto _mc_failed;
+
+    dir = _vfile_path_to_menu_cache_item(mc, init->path_str);
+    if(dir == NULL)
+        g_set_error_literal(init->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                            _("Invalid menu item"));
+    else if (menu_cache_item_get_file_basename(dir) == NULL ||
+             menu_cache_item_get_file_dirname(dir) == NULL)
+        g_set_error(init->error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                    _("The menu item '%s' doesn't have appropriate entry file"),
+                    menu_cache_item_get_id(dir));
+    else if (!g_cancellable_set_error_if_cancelled(init->cancellable, init->error))
+    {
+        char *path = menu_cache_item_get_file_path(dir);
+        GKeyFile *kf = g_key_file_new();
+
+        ok = g_key_file_load_from_file(kf, path, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
+                                       init->error);
+        g_free(path);
+        if (ok)
+        {
+            /* get locale name */
+            const gchar * const *langs = g_get_language_names();
+            char *contents;
+            gsize length;
+
+            if (strcmp(langs[0], "C") != 0)
+            {
+                char *lang;
+                /* remove encoding from locale name */
+                char *sep = strchr(langs[0], '.');
+
+                if (sep)
+                    lang = g_strndup(langs[0], sep - langs[0]);
+                else
+                    lang = g_strdup(langs[0]);
+                g_key_file_set_locale_string(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                             G_KEY_FILE_DESKTOP_KEY_NAME, lang,
+                                             init->display_name);
+                g_free(lang);
+            }
+            else
+                g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                      G_KEY_FILE_DESKTOP_KEY_NAME, init->display_name);
+            contents = g_key_file_to_data(kf, &length, init->error);
+            if (contents == NULL)
+                ok = FALSE;
+            else
+            {
+                path = g_build_filename(g_get_user_data_dir(),
+                                        (menu_cache_item_get_type(dir) == MENU_CACHE_TYPE_DIR) ? "desktop-directories" : "applications",
+                                        menu_cache_item_get_file_basename(dir), NULL);
+                ok = g_file_set_contents(path, contents, length, init->error);
+                /* FIXME: handle case if directory doesn't exist */
+                g_free(contents);
+                g_free(path);
+            }
+        }
+        g_key_file_free(kf);
+    }
+
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    if(dir)
+        menu_cache_item_unref(dir);
+#endif
+    menu_cache_unref(mc);
+
+_mc_failed:
+    return ok;
+}
+
+static GFile *_fm_vfs_menu_set_display_name(GFile *file,
+                                            const char *display_name,
+                                            GCancellable *cancellable,
+                                            GError **error)
+{
+    FmMenuVFile *item = FM_MENU_VFILE(file);
+    FmVfsMenuMainThreadData enu;
+
+    /* g_debug("_fm_vfs_menu_set_display_name: %s -> %s", item->path, display_name); */
+    if (item->path == NULL)
+    {
+        ERROR_UNSUPPORTED(error);
+        return NULL;
+    }
+    if (display_name == NULL || *display_name == '\0')
+    {
+        g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                            _("Menu item name cannot be empty"));
+        return NULL;
+    }
+    enu.path_str = item->path;
+    enu.display_name = display_name;
+    enu.cancellable = cancellable;
+    enu.error = error;
+    if (RUN_WITH_MENU_CACHE(_fm_vfs_menu_set_display_name_real, &enu))
+        return g_object_ref(file);
+    return NULL;
+}
+
+static GFileAttributeInfoList *_fm_vfs_menu_query_settable_attributes(GFile *file,
+                                                                      GCancellable *cancellable,
+                                                                      GError **error)
+{
+    return g_file_attribute_info_list_ref(_fm_vfs_menu_settable_attributes);
+}
+
+static GFileAttributeInfoList *_fm_vfs_menu_query_writable_namespaces(GFile *file,
+                                                                      GCancellable *cancellable,
+                                                                      GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static gboolean _fm_vfs_menu_set_attributes_from_info_real(gpointer data)
+{
+    FmVfsMenuMainThreadData *init = data;
+    MenuCache *mc;
+    MenuCacheItem *item;
+    gpointer value;
+    const char *display_name = NULL;
+    GIcon *icon = NULL;
+    gint set_hidden = -1;
+    gboolean ok = FALSE;
+
+    /* check attributes first */
+    if (g_file_info_get_attribute_data(init->info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+                                       NULL, &value, NULL))
+        display_name = value;
+    if (g_file_info_get_attribute_data(init->info, G_FILE_ATTRIBUTE_STANDARD_ICON,
+                                       NULL, &value, NULL))
+        icon = value;
+    if (g_file_info_get_attribute_data(init->info, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
+                                       NULL, &value, NULL))
+        set_hidden = (*(gboolean *)value) ? 1 : 0;
+    if (display_name == NULL && icon == NULL && set_hidden < 0)
+        return TRUE; /* nothing to do */
+    /* now try access item */
+    mc = _get_menu_cache(init->error);
+    if(mc == NULL)
+        goto _mc_failed;
+
+    item = _vfile_path_to_menu_cache_item(mc, init->path_str);
+    if(item == NULL)
+        g_set_error_literal(init->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                            _("Invalid menu item"));
+    else if (menu_cache_item_get_file_basename(item) == NULL ||
+             menu_cache_item_get_file_dirname(item) == NULL)
+        g_set_error(init->error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                    _("The menu item '%s' doesn't have appropriate entry file"),
+                    menu_cache_item_get_id(item));
+    else if (!g_cancellable_set_error_if_cancelled(init->cancellable, init->error))
+    {
+        char *path;
+        GKeyFile *kf;
+        GError *err = NULL;
+        gboolean no_error = TRUE;
+
+        /* for hidden on directory: use _add_directory() or _remove_directory() */
+        if (set_hidden >= 0 && menu_cache_item_get_type(item) == MENU_CACHE_TYPE_DIR)
+        {
+#if MENU_CACHE_CHECK_VERSION(0, 5, 0)
+            char *unescaped = g_uri_unescape_string(init->path_str, NULL);
+            if (set_hidden > 0)
+                no_error = _remove_directory(unescaped, init->cancellable, init->error);
+            else
+                no_error = _add_directory(unescaped, init->cancellable, init->error);
+            g_free(unescaped);
+            ok = no_error;
+#else
+            g_set_error_literal(init->error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                                _("Change hidden status isn't supported for menu directory"));
+            no_error = FALSE;
+#endif
+            if (display_name == NULL && icon == NULL) /* nothing else to update */
+                goto _done;
+            set_hidden = -1; /* don't set NoDisplay for a directory */
+        }
+        /* in all other cases - update Name, Icon or NoDisplay and save keyfile */
+        path = menu_cache_item_get_file_path(item);
+        kf = g_key_file_new();
+        ok = g_key_file_load_from_file(kf, path, G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
+                                       &err);
+        g_free(path);
+        if (ok) /* otherwise there is no reason to continue */
+        {
+            char *contents;
+            gsize length;
+
+            if (display_name)
+            {
+                /* get locale name */
+                const gchar * const *langs = g_get_language_names();
+
+                if (strcmp(langs[0], "C") != 0)
+                {
+                    char *lang;
+                    /* remove encoding from locale name */
+                    char *sep = strchr(langs[0], '.');
+
+                    if (sep)
+                        lang = g_strndup(langs[0], sep - langs[0]);
+                    else
+                        lang = g_strdup(langs[0]);
+                    g_key_file_set_locale_string(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                                 G_KEY_FILE_DESKTOP_KEY_NAME, lang,
+                                                 display_name);
+                    g_free(lang);
+                }
+                else
+                    g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                          G_KEY_FILE_DESKTOP_KEY_NAME, display_name);
+            }
+            if (icon)
+            {
+                char *icon_str = g_icon_to_string(icon);
+                /* FIXME: need to change encoding in some cases? */
+                g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                      G_KEY_FILE_DESKTOP_KEY_ICON, icon_str);
+                g_free(icon_str);
+            }
+            if (set_hidden >= 0)
+            {
+                g_key_file_set_boolean(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                       G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY,
+                                       (set_hidden > 0));
+            }
+            contents = g_key_file_to_data(kf, &length, &err);
+            if (contents == NULL)
+                ok = FALSE;
+            else
+            {
+                path = g_build_filename(g_get_user_data_dir(),
+                                        (menu_cache_item_get_type(item) == MENU_CACHE_TYPE_DIR) ? "desktop-directories" : "applications",
+                                        menu_cache_item_get_file_basename(item), NULL);
+                ok = g_file_set_contents(path, contents, length, &err);
+                /* FIXME: handle case if directory doesn't exist */
+                g_free(contents);
+                g_free(path);
+            }
+        }
+        g_key_file_free(kf);
+        if (no_error && !ok) /* we got error in err */
+            g_propagate_error(init->error, err);
+        else if (!ok) /* both init->error and err contain error */
+            g_error_free(err);
+        else if (!no_error) /* we got error in init->error */
+            ok = FALSE;
+    }
+
+_done:
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    if(item)
+        menu_cache_item_unref(item);
+#endif
+    menu_cache_unref(mc);
+
+_mc_failed:
+    return ok;
+}
+
+static gboolean _fm_vfs_menu_set_attributes_from_info(GFile *file,
+                                                      GFileInfo *info,
+                                                      GFileQueryInfoFlags flags,
+                                                      GCancellable *cancellable,
+                                                      GError **error)
+{
+    FmMenuVFile *item = FM_MENU_VFILE(file);
+    FmVfsMenuMainThreadData enu;
+
+    if (item->path == NULL)
+    {
+        ERROR_UNSUPPORTED(error);
+        return FALSE;
+    }
+    enu.path_str = item->path;
+    enu.info = info;
+//    enu.flags = flags;
+    enu.cancellable = cancellable;
+    enu.error = error;
+    return (RUN_WITH_MENU_CACHE(_fm_vfs_menu_set_attributes_from_info_real, &enu));
+}
+
+static gboolean _fm_vfs_menu_set_attribute(GFile *file,
+                                           const char *attribute,
+                                           GFileAttributeType type,
+                                           gpointer value_p,
+                                           GFileQueryInfoFlags flags,
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+    FmMenuVFile *item = FM_MENU_VFILE(file);
+    GFileInfo *info;
+    gboolean result;
+
+    g_debug("_fm_vfs_menu_set_attribute: %s on %s", attribute, item->path);
+    if (item->path == NULL)
+    {
+        ERROR_UNSUPPORTED(error);
+        return FALSE;
+    }
+    if (value_p == NULL)
+        goto _invalid_arg;
+    if (strcmp(attribute, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME) == 0)
+    {
+        if (type != G_FILE_ATTRIBUTE_TYPE_STRING)
+            goto _invalid_arg;
+        info = g_file_info_new();
+        g_file_info_set_display_name(info, value_p);
+    }
+    else if (strcmp(attribute, G_FILE_ATTRIBUTE_STANDARD_ICON) == 0)
+    {
+        if (type != G_FILE_ATTRIBUTE_TYPE_OBJECT)
+            goto _invalid_arg;
+        if (!G_IS_ICON(value_p))
+            goto _invalid_arg;
+        info = g_file_info_new();
+        g_file_info_set_icon(info, value_p);
+    }
+    else if (strcmp(attribute, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN) == 0)
+    {
+        if (type != G_FILE_ATTRIBUTE_TYPE_BOOLEAN)
+            goto _invalid_arg;
+        info = g_file_info_new();
+        g_file_info_set_is_hidden(info, *(gboolean *)value_p);
+    }
+    else
+    {
+        g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                    _("Setting attribute '%s' not supported"), attribute);
+        return FALSE;
+    }
+    result = _fm_vfs_menu_set_attributes_from_info(file, info, flags,
+                                                   cancellable, error);
+    g_object_unref(info);
+    return result;
+
+_invalid_arg:
+    g_set_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                _("Invalid value for attribute '%s'"), attribute);
+    return FALSE;
+}
+
+static inline GFile *_g_file_new_for_id(const char *id)
+{
+    char *file_path;
+    GFile *file;
+
+    file_path = g_build_filename(g_get_user_data_dir(), "applications", id, NULL);
+    /* we can try to guess file path and make directories but it
+       hardly worth the efforts so it's easier to just make new file
+       by its ID since ID is unique thru all the menu */
+    if (file_path == NULL)
+        return NULL;
+    file = g_file_new_for_path(file_path);
+    g_free(file_path);
+    return file;
+}
+
+static gboolean _fm_vfs_menu_read_fn_real(gpointer data)
+{
+    FmVfsMenuMainThreadData *init = data;
+    MenuCache *mc;
+    MenuCacheItem *item = NULL;
+
+    init->result = NULL;
+    mc = _get_menu_cache(init->error);
+    if(mc == NULL)
+        goto _mc_failed;
+
+    if(init->path_str)
+        item = _vfile_path_to_menu_cache_item(mc, init->path_str);
+
+        /* If item wasn't found or isn't a file then we cannot read it. */
+    if(item != NULL && menu_cache_item_get_type(item) == MENU_CACHE_TYPE_DIR)
+        g_set_error(init->error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
+                    _("The '%s' is a menu directory"), init->path_str);
+    else if(item == NULL || menu_cache_item_get_type(item) != MENU_CACHE_TYPE_APP)
+        g_set_error(init->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                    _("The '%s' isn't a menu item"),
+                    init->path_str ? init->path_str : "/");
+    else
+    {
+        char *file_path;
+        GFile *gf;
+        GError *err = NULL;
+
+        file_path = menu_cache_item_get_file_path(item);
+        if (file_path)
+        {
+            gf = g_file_new_for_path(file_path);
+            g_free(file_path);
+            if (gf)
+            {
+                init->result = g_file_read(gf, init->cancellable, &err);
+                if (init->result == NULL)
+                {
+                    /* never return G_IO_ERROR_IS_DIRECTORY */
+                    if (err->domain == G_IO_ERROR &&
+                        err->code == G_IO_ERROR_IS_DIRECTORY)
+                    {
+                        g_error_free(err);
+                        g_set_error(init->error, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
+                                    _("The '%s' entry file is broken"), init->path_str);
+                    }
+                    else
+                        g_propagate_error(init->error, err);
+                }
+                g_object_unref(gf);
+            }
+        }
+    }
+
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    if(item)
+        menu_cache_item_unref(item);
+#endif
+    menu_cache_unref(mc);
+
+_mc_failed:
+    return FALSE;
+}
+
+static GFileInputStream *_fm_vfs_menu_read_fn(GFile *file,
+                                              GCancellable *cancellable,
+                                              GError **error)
+{
+    FmMenuVFile *item = FM_MENU_VFILE(file);
+    FmVfsMenuMainThreadData enu;
+
+    /* g_debug("_fm_vfs_menu_read_fn %s", item->path); */
+    enu.path_str = item->path;
+    enu.cancellable = cancellable;
+    enu.error = error;
+    RUN_WITH_MENU_CACHE(_fm_vfs_menu_read_fn_real, &enu);
+    return enu.result;
+}
+
+static GFileOutputStream *_fm_vfs_menu_append_to(GFile *file,
+                                                 GFileCreateFlags flags,
+                                                 GCancellable *cancellable,
+                                                 GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+
+/* ---- FmMenuVFileOutputStream class ---- */
+#define FM_TYPE_MENU_VFILE_OUTPUT_STREAM  (fm_vfs_menu_file_output_stream_get_type())
+#define FM_MENU_VFILE_OUTPUT_STREAM(o)    (G_TYPE_CHECK_INSTANCE_CAST((o), \
+                                           FM_TYPE_MENU_VFILE_OUTPUT_STREAM, \
+                                           FmMenuVFileOutputStream))
+
+typedef struct _FmMenuVFileOutputStream      FmMenuVFileOutputStream;
+typedef struct _FmMenuVFileOutputStreamClass FmMenuVFileOutputStreamClass;
+
+struct _FmMenuVFileOutputStream
+{
+    GFileOutputStream parent;
+    GOutputStream *real_stream;
+    gchar *path; /* "Dir/App.desktop" */
+    GString *content;
+    gboolean do_close;
+};
+
+struct _FmMenuVFileOutputStreamClass
+{
+    GFileOutputStreamClass parent_class;
+};
+
+static GType fm_vfs_menu_file_output_stream_get_type  (void);
+
+G_DEFINE_TYPE(FmMenuVFileOutputStream, fm_vfs_menu_file_output_stream, G_TYPE_FILE_OUTPUT_STREAM);
+
+static void fm_vfs_menu_file_output_stream_finalize(GObject *object)
+{
+    FmMenuVFileOutputStream *stream = FM_MENU_VFILE_OUTPUT_STREAM(object);
+    if(stream->real_stream)
+        g_object_unref(stream->real_stream);
+    g_free(stream->path);
+    g_string_free(stream->content, TRUE);
+    G_OBJECT_CLASS(fm_vfs_menu_file_output_stream_parent_class)->finalize(object);
+}
+
+static gssize fm_vfs_menu_file_output_stream_write(GOutputStream *stream,
+                                                   const void *buffer, gsize count,
+                                                   GCancellable *cancellable,
+                                                   GError **error)
+{
+    if (g_cancellable_set_error_if_cancelled(cancellable, error))
+        return -1;
+    g_string_append_len(FM_MENU_VFILE_OUTPUT_STREAM(stream)->content, buffer, count);
+    return (gssize)count;
+}
+
+static gboolean fm_vfs_menu_file_output_stream_close(GOutputStream *gos,
+                                                     GCancellable *cancellable,
+                                                     GError **error)
+{
+    FmMenuVFileOutputStream *stream = FM_MENU_VFILE_OUTPUT_STREAM(gos);
+    GKeyFile *kf;
+    gsize len = 0;
+    gchar *content;
+    gboolean ok;
+
+    if (g_cancellable_set_error_if_cancelled(cancellable, error))
+        return FALSE;
+    if (!stream->do_close)
+        return TRUE;
+    kf = g_key_file_new();
+    /* parse entered file content first */
+    if (stream->content->len > 0)
+        g_key_file_load_from_data(kf, stream->content->str, stream->content->len,
+                                  G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
+                                  NULL); /* FIXME: don't ignore some errors? */
+    /* correct invalid data in desktop entry file: Name and Exec are mandatory,
+       Type must be Application, and Category should include requested one */
+    if(!g_key_file_has_key(kf, G_KEY_FILE_DESKTOP_GROUP,
+                           G_KEY_FILE_DESKTOP_KEY_NAME, NULL))
+        g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP,
+                              G_KEY_FILE_DESKTOP_KEY_NAME, "");
+    if(!g_key_file_has_key(kf, G_KEY_FILE_DESKTOP_GROUP,
+                           G_KEY_FILE_DESKTOP_KEY_EXEC, NULL))
+        g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP,
+                              G_KEY_FILE_DESKTOP_KEY_EXEC, "");
+    g_key_file_set_string(kf, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TYPE,
+                          G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
+    content = g_key_file_to_data(kf, &len, error);
+    g_key_file_free(kf);
+    if (!content)
+        return FALSE;
+    ok = g_output_stream_write_all(stream->real_stream, content, len, &len,
+                                   cancellable, error);
+    g_free(content);
+    if (!ok || !g_output_stream_close(stream->real_stream, cancellable, error))
+        return FALSE;
+    stream->do_close = FALSE;
+    if (stream->path && !_add_application(stream->path, cancellable, error))
+        return FALSE;
+    return TRUE;
+}
+
+static void fm_vfs_menu_file_output_stream_class_init(FmMenuVFileOutputStreamClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+    GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS(klass);
+
+    gobject_class->finalize = fm_vfs_menu_file_output_stream_finalize;
+    stream_class->write_fn = fm_vfs_menu_file_output_stream_write;
+    stream_class->close_fn = fm_vfs_menu_file_output_stream_close;
+    /* we don't implement seek/truncate/etag/query so no GFileOutputStream funcs */
+}
+
+static void fm_vfs_menu_file_output_stream_init(FmMenuVFileOutputStream *stream)
+{
+    stream->content = g_string_sized_new(1024);
+    stream->do_close = TRUE;
+}
+
+static FmMenuVFileOutputStream *_fm_vfs_menu_file_output_stream_new(const gchar *path)
+{
+    FmMenuVFileOutputStream *stream;
+
+    stream = g_object_new(FM_TYPE_MENU_VFILE_OUTPUT_STREAM, NULL);
+    if (path)
+        stream->path = g_strdup(path);
+    return stream;
+}
+
+static GFileOutputStream *_vfile_menu_create(GFile *file,
+                                             GFileCreateFlags flags,
+                                             GCancellable *cancellable,
+                                             GError **error,
+                                             const gchar *path)
+{
+    FmMenuVFileOutputStream *stream;
+    GFileOutputStream *ostream;
+    GError *err = NULL;
+    GFile *parent;
+
+    if (g_cancellable_set_error_if_cancelled(cancellable, error))
+        return NULL;
+//    g_file_delete(file, cancellable, NULL); /* remove old if there is any */
+    ostream = g_file_create(file, flags, cancellable, &err);
+    if (ostream == NULL)
+    {
+        if (g_cancellable_is_cancelled(cancellable) ||
+            err->domain != G_IO_ERROR || err->code != G_IO_ERROR_NOT_FOUND)
+        {
+            g_propagate_error(error, err);
+            return NULL;
+        }
+        /* .local/share/applications/ isn't found? make it! */
+        g_clear_error(&err);
+        parent = g_file_get_parent(file);
+        if (!g_file_make_directory_with_parents(parent, cancellable, error))
+        {
+            g_object_unref(parent);
+            return NULL;
+        }
+        g_object_unref(parent);
+        ostream = g_file_create(file, flags, cancellable, error);
+        if (ostream == NULL)
+            return ostream;
+    }
+    stream = _fm_vfs_menu_file_output_stream_new(path);
+    stream->real_stream = G_OUTPUT_STREAM(ostream);
+    return (GFileOutputStream*)stream;
+}
+
+static GFileOutputStream *_vfile_menu_replace(GFile *file,
+                                              const char *etag,
+                                              gboolean make_backup,
+                                              GFileCreateFlags flags,
+                                              GCancellable *cancellable,
+                                              GError **error,
+                                              const gchar *path)
+{
+    FmMenuVFileOutputStream *stream;
+    GFileOutputStream *ostream;
+
+    if (g_cancellable_set_error_if_cancelled(cancellable, error))
+        return NULL;
+    stream = _fm_vfs_menu_file_output_stream_new(path);
+    ostream = g_file_replace(file, etag, make_backup, flags, cancellable, error);
+    if (ostream == NULL)
+    {
+        g_object_unref(stream);
+        return NULL;
+    }
+    stream->real_stream = G_OUTPUT_STREAM(ostream);
+    return (GFileOutputStream*)stream;
+}
+
+static gboolean _fm_vfs_menu_create_real(gpointer data)
+{
+    FmVfsMenuMainThreadData *init = data;
+    MenuCache *mc;
+    char *unescaped = NULL, *id;
+    gboolean is_invalid = TRUE;
+
+    init->result = NULL;
+    if(init->path_str)
+    {
+        MenuCacheItem *item;
+#if !MENU_CACHE_CHECK_VERSION(0, 5, 0)
+        GSList *list, *l;
+#endif
+
+        mc = _get_menu_cache(init->error);
+        if(mc == NULL)
+            goto _mc_failed;
+        unescaped = g_uri_unescape_string(init->path_str, NULL);
+        /* ensure new menu item has suffix .desktop */
+        if (!g_str_has_suffix(unescaped, ".desktop"))
+        {
+            id = unescaped;
+            unescaped = g_strconcat(unescaped, ".desktop", NULL);
+            g_free(id);
+        }
+        id = strrchr(unescaped, '/');
+        if (id)
+            id++;
+        else
+            id = unescaped;
+#if MENU_CACHE_CHECK_VERSION(0, 5, 0)
+        item = menu_cache_find_item_by_id(mc, id);
+        if (item)
+            menu_cache_item_unref(item); /* use item simply as marker */
+#else
+        list = menu_cache_list_all_apps(mc);
+        for (l = list; l; l = l->next)
+            if (strcmp(menu_cache_item_get_id(l->data), id) == 0)
+                break;
+        if (l)
+            item = l->data;
+        else
+            item = NULL;
+        g_slist_free_full(list, (GDestroyNotify)menu_cache_item_unref);
+#endif
+        if(item == NULL)
+            is_invalid = FALSE;
+        /* g_debug("create id %s, category %s", id, category); */
+        menu_cache_unref(mc);
+    }
+
+    if(is_invalid)
+        g_set_error(init->error, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                    _("Cannot create menu item '%s'"),
+                    init->path_str ? init->path_str : "/");
+    else
+    {
+        GFile *gf = _g_file_new_for_id(id);
+
+        if (gf)
+        {
+            init->result = _vfile_menu_create(gf, G_FILE_CREATE_NONE,
+                                              init->cancellable, init->error,
+                                              unescaped);
+            g_object_unref(gf);
+        }
+    }
+    g_free(unescaped);
+
+_mc_failed:
+    return FALSE;
+}
+
+static GFileOutputStream *_fm_vfs_menu_create(GFile *file,
+                                              GFileCreateFlags flags,
+                                              GCancellable *cancellable,
+                                              GError **error)
+{
+    FmMenuVFile *item = FM_MENU_VFILE(file);
+    FmVfsMenuMainThreadData enu;
+
+    /* g_debug("_fm_vfs_menu_create %s", item->path); */
+    enu.path_str = item->path;
+    enu.cancellable = cancellable;
+    enu.error = error;
+    // enu.flags = flags;
+    RUN_WITH_MENU_CACHE(_fm_vfs_menu_create_real, &enu);
+    return enu.result;
+}
+
+static gboolean _fm_vfs_menu_replace_real(gpointer data)
+{
+    FmVfsMenuMainThreadData *init = data;
+    MenuCache *mc;
+    char *unescaped = NULL, *id;
+    gboolean is_invalid = TRUE;
+
+    init->result = NULL;
+    if(init->path_str)
+    {
+        MenuCacheItem *item, *item2;
+
+        mc = _get_menu_cache(init->error);
+        if(mc == NULL)
+            goto _mc_failed;
+        /* prepare id first */
+        unescaped = g_uri_unescape_string(init->path_str, NULL);
+        id = strrchr(unescaped, '/');
+        if (id != NULL)
+            id++;
+        else
+            id = unescaped;
+        /* get existing item */
+        item = _vfile_path_to_menu_cache_item(mc, init->path_str);
+        if (item != NULL) /* item is there, OK, we'll replace it then */
+            is_invalid = FALSE;
+        /* if not found then check item by id to exclude conflicts */
+        else
+        {
+#if MENU_CACHE_CHECK_VERSION(0, 5, 0)
+            item2 = menu_cache_find_item_by_id(mc, id);
+#else
+            GSList *list = menu_cache_list_all_apps(mc), *l;
+            for (l = list; l; l = l->next)
+                if (strcmp(menu_cache_item_get_id(l->data), id) == 0)
+                    break;
+            if (l)
+                item2 = menu_cache_item_ref(l->data);
+            else
+                item2 = NULL;
+            g_slist_free_full(list, (GDestroyNotify)menu_cache_item_unref);
+#endif
+            if(item2 == NULL)
+                is_invalid = FALSE;
+            else /* item was found in another category */
+                menu_cache_item_unref(item2);
+        }
+        menu_cache_unref(mc);
+    }
+
+    if(is_invalid)
+        g_set_error(init->error, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                    _("Cannot create menu item '%s'"),
+                    init->path_str ? init->path_str : "/");
+    else
+    {
+        GFile *gf = _g_file_new_for_id(id);
+
+        if (gf)
+        {
+            /* FIXME: use flags and make_backup */
+            init->result = _vfile_menu_replace(gf, NULL, FALSE,
+                                               G_FILE_CREATE_REPLACE_DESTINATION,
+                                               init->cancellable, init->error,
+                                               /* don't insert it into XML */
+                                               NULL);
+            g_object_unref(gf);
+        }
+    }
+    g_free(unescaped);
+
+_mc_failed:
+    return FALSE;
+}
+
+static GFileOutputStream *_fm_vfs_menu_replace(GFile *file,
+                                               const char *etag,
+                                               gboolean make_backup,
+                                               GFileCreateFlags flags,
+                                               GCancellable *cancellable,
+                                               GError **error)
+{
+    FmMenuVFile *item = FM_MENU_VFILE(file);
+    FmVfsMenuMainThreadData enu;
+
+    /* g_debug("_fm_vfs_menu_replace %s", item->path); */
+    enu.path_str = item->path;
+    enu.cancellable = cancellable;
+    enu.error = error;
+    // enu.flags = flags;
+    // enu.make_backup = make_backup;
+    RUN_WITH_MENU_CACHE(_fm_vfs_menu_replace_real, &enu);
+    return enu.result;
+}
+
+/* not in main thread; returns NULL on failure */
+static GKeyFile *_g_key_file_from_item(GFile *file, GCancellable *cancellable,
+                                       GError **error)
+{
+    char *contents;
+    gsize length;
+    GKeyFile *kf;
+
+    if (!g_file_load_contents(file, cancellable, &contents, &length, NULL, error))
+        return NULL;
+    kf = g_key_file_new();
+    if (!g_key_file_load_from_data(kf, contents, length,
+                                   G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS,
+                                   error))
+    {
+        g_key_file_free(kf);
+        kf = NULL;
+    }
+    g_free(contents);
+    return kf;
+}
+
+/* not in main thread; returns FALSE on failure, consumes key file */
+static gboolean _g_key_file_into_item(GFile *file, GKeyFile *kf,
+                                      GCancellable *cancellable, GError **error)
+{
+    char *contents;
+    gsize length;
+    gboolean result = FALSE;
+
+    contents = g_key_file_to_data(kf, &length, error);
+    g_key_file_free(kf);
+    if (contents == NULL)
+        return FALSE;
+    result = g_file_replace_contents(file, contents, length, NULL, FALSE,
+                                     G_FILE_CREATE_REPLACE_DESTINATION, NULL,
+                                     cancellable, error);
+    g_free(contents);
+    return result;
+}
+
+static gboolean _fm_vfs_menu_delete_file(GFile *file,
+                                         GCancellable *cancellable,
+                                         GError **error)
+{
+    FmMenuVFile *item = FM_MENU_VFILE(file);
+    GKeyFile *kf;
+    GError *err = NULL;
+
+    g_debug("_fm_vfs_menu_delete_file %s", item->path);
+    /* load contents */
+    kf = _g_key_file_from_item(file, cancellable, &err);
+    if (kf == NULL)
+    {
+#if MENU_CACHE_CHECK_VERSION(0, 5, 0)
+        /* it might be just a directory */
+        if (err->domain == G_IO_ERROR && err->code == G_IO_ERROR_IS_DIRECTORY)
+        {
+            char *unescaped = g_uri_unescape_string(item->path, NULL);
+            gboolean ok = _remove_directory(unescaped, cancellable, error);
+            g_error_free(err);
+            g_free(unescaped);
+            return ok;
+        }
+        /* else it just failed */
+#endif
+        g_propagate_error(error, err);
+        return FALSE;
+    }
+    /* set NoDisplay=true and save */
+    g_key_file_set_boolean(kf, G_KEY_FILE_DESKTOP_GROUP,
+                           G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
+    return _g_key_file_into_item(file, kf, cancellable, error);
+}
+
+static gboolean _fm_vfs_menu_trash(GFile *file,
+                                   GCancellable *cancellable,
+                                   GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return FALSE;
+}
+
+static gboolean _fm_vfs_menu_make_directory(GFile *file,
+                                            GCancellable *cancellable,
+                                            GError **error)
+{
+#if !MENU_CACHE_CHECK_VERSION(0, 5, 0)
+    /* creating a directory with libmenu-cache < 0.5.0 will lead to invisible
+       directory; inexperienced user will be confused; therefore we disable
+       such operation in such conditions */
+    ERROR_UNSUPPORTED(error);
+    return FALSE;
+#else
+    FmMenuVFile *item = FM_MENU_VFILE(file);
+    char *unescaped;
+    gboolean ok;
+
+    /* XDG desktop menu specification: desktop-entry-id should be *.desktop */
+    if (g_str_has_suffix(item->path, ".desktop"))
+    {
+        g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME,
+                            _("Name of menu directory should not end with"
+                              " \".desktop\""));
+        return FALSE;
+    }
+    unescaped = g_uri_unescape_string(item->path, NULL);
+    ok = _add_directory(unescaped, cancellable, error);
+    g_free(unescaped);
+    return ok;
+#endif
+}
+
+static gboolean _fm_vfs_menu_make_symbolic_link(GFile *file,
+                                                const char *symlink_value,
+                                                GCancellable *cancellable,
+                                                GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return FALSE;
+}
+
+static gboolean _fm_vfs_menu_copy(GFile *source,
+                                  GFile *destination,
+                                  GFileCopyFlags flags,
+                                  GCancellable *cancellable,
+                                  GFileProgressCallback progress_callback,
+                                  gpointer progress_callback_data,
+                                  GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return FALSE;
+}
+
+static gboolean _fm_vfs_menu_move_real(gpointer data)
+{
+    FmVfsMenuMainThreadData *init = data;
+    MenuCache *mc = NULL;
+    MenuCacheItem *item = NULL, *item2;
+    char *src_path, *dst_path;
+    char *src_id, *dst_id;
+    gboolean result = FALSE;
+
+    dst_path = init->destination->path;
+    if (init->path_str == NULL || dst_path == NULL)
+    {
+        g_set_error_literal(init->error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                            _("Invalid operation with menu root"));
+        return FALSE;
+    }
+    /* make path strings */
+    src_path = g_uri_unescape_string(init->path_str, NULL);
+    dst_path = g_uri_unescape_string(dst_path, NULL);
+    src_id = strrchr(src_path, '/');
+    if (src_id)
+        src_id++;
+    else
+        src_id = src_path;
+    dst_id = strrchr(dst_path, '/');
+    if (dst_id)
+        dst_id++;
+    else
+        dst_id = dst_path;
+    if (strcmp(src_id, dst_id))
+    {
+        /* ID change isn't supported now */
+        ERROR_UNSUPPORTED(init->error);
+        goto _failed;
+    }
+    if (strcmp(src_path, dst_path) == 0)
+    {
+        g_warning("menu: tried to move '%s' into itself", src_path);
+        g_free(src_path);
+        g_free(dst_path);
+        return TRUE; /* nothing was changed */
+    }
+    /* do actual move */
+    mc = _get_menu_cache(init->error);
+    if(mc == NULL)
+        goto _failed;
+    item = _vfile_path_to_menu_cache_item(mc, init->path_str);
+    /* TODO: if id changed then check for ID conflicts */
+    /* TODO: save updated desktop entry for old ID (if different) */
+    if(item == NULL || menu_cache_item_get_type(item) != MENU_CACHE_TYPE_APP)
+    {
+        /* FIXME: implement directories movement */
+        g_set_error(init->error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                    _("The '%s' isn't a menu item"), init->path_str);
+        goto _failed;
+    }
+    item2 = _vfile_path_to_menu_cache_item(mc, init->destination->path);
+    if (item2)
+    {
+        g_set_error(init->error, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                    _("Menu path '%s' already exists"), dst_path);
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+        menu_cache_item_unref(item2);
+#endif
+        goto _failed;
+    }
+    /* do actual move */
+    if (_add_application(dst_path, init->cancellable, init->error))
+    {
+        if (_remove_application(src_path, init->cancellable, init->error))
+            result = TRUE;
+        else /* failed, rollback */
+            _remove_application(dst_path, init->cancellable, NULL);
+    }
+
+_failed:
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    if(item)
+        menu_cache_item_unref(item);
+#endif
+    if(mc)
+        menu_cache_unref(mc);
+    g_free(src_path);
+    g_free(dst_path);
+    return result;
+}
+
+static gboolean _fm_vfs_menu_move(GFile *source,
+                                  GFile *destination,
+                                  GFileCopyFlags flags,
+                                  GCancellable *cancellable,
+                                  GFileProgressCallback progress_callback,
+                                  gpointer progress_callback_data,
+                                  GError **error)
+{
+    FmMenuVFile *item = FM_MENU_VFILE(source);
+    FmVfsMenuMainThreadData enu;
+
+    /* g_debug("_fm_vfs_menu_move"); */
+    if(!FM_IS_FILE(destination))
+    {
+        g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                            _("Invalid destination"));
+        return FALSE;
+    }
+    enu.path_str = item->path;
+    enu.cancellable = cancellable;
+    enu.error = error;
+    // enu.flags = flags;
+    enu.destination = FM_MENU_VFILE(destination);
+    /* FIXME: use progress_callback */
+    return RUN_WITH_MENU_CACHE(_fm_vfs_menu_move_real, &enu);
+}
+
+/* ---- FmMenuVFileMonitor class ---- */
+#define FM_TYPE_MENU_VFILE_MONITOR     (fm_vfs_menu_file_monitor_get_type())
+#define FM_MENU_VFILE_MONITOR(o)       (G_TYPE_CHECK_INSTANCE_CAST((o), \
+                                        FM_TYPE_MENU_VFILE_MONITOR, FmMenuVFileMonitor))
+
+typedef struct _FmMenuVFileMonitor      FmMenuVFileMonitor;
+typedef struct _FmMenuVFileMonitorClass FmMenuVFileMonitorClass;
+
+static GType fm_vfs_menu_file_monitor_get_type  (void);
+
+struct _FmMenuVFileMonitor
+{
+    GFileMonitor parent_object;
+
+    FmMenuVFile *file;
+    MenuCache *cache;
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    MenuCacheItem *item;
+    MenuCacheNotifyId notifier;
+#else
+    GSList *items;
+    gboolean stopped;
+    gpointer notifier;
+#endif
+};
+
+struct _FmMenuVFileMonitorClass
+{
+    GFileMonitorClass parent_class;
+};
+
+G_DEFINE_TYPE(FmMenuVFileMonitor, fm_vfs_menu_file_monitor, G_TYPE_FILE_MONITOR);
+
+static void fm_vfs_menu_file_monitor_finalize(GObject *object)
+{
+    FmMenuVFileMonitor *mon = FM_MENU_VFILE_MONITOR(object);
+
+    if(mon->cache)
+    {
+        if(mon->notifier)
+            menu_cache_remove_reload_notify(mon->cache, mon->notifier);
+        menu_cache_unref(mon->cache);
+    }
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    if(mon->item)
+        menu_cache_item_unref(mon->item);
+#else
+    g_slist_free_full(mon->items, (GDestroyNotify)menu_cache_item_unref);
+#endif
+    g_object_unref(mon->file);
+
+    G_OBJECT_CLASS(fm_vfs_menu_file_monitor_parent_class)->finalize(object);
+}
+
+static gboolean fm_vfs_menu_file_monitor_cancel(GFileMonitor *monitor)
+{
+    FmMenuVFileMonitor *mon = FM_MENU_VFILE_MONITOR(monitor);
+
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    if(mon->item)
+        menu_cache_item_unref(mon->item); /* rest will be done in finalizer */
+    mon->item = NULL;
+#else
+    mon->stopped = TRUE;
+#endif
+    return TRUE;
+}
+
+static void fm_vfs_menu_file_monitor_class_init(FmMenuVFileMonitorClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+    GFileMonitorClass *gfilemon_class = G_FILE_MONITOR_CLASS (klass);
+
+    gobject_class->finalize = fm_vfs_menu_file_monitor_finalize;
+    gfilemon_class->cancel = fm_vfs_menu_file_monitor_cancel;
+}
+
+static void fm_vfs_menu_file_monitor_init(FmMenuVFileMonitor *item)
+{
+    /* nothing */
+}
+
+static FmMenuVFileMonitor *_fm_menu_vfile_monitor_new(void)
+{
+    return (FmMenuVFileMonitor*)g_object_new(FM_TYPE_MENU_VFILE_MONITOR, NULL);
+}
+
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+static void _reload_notify_handler(MenuCache* cache, gpointer user_data)
+#else
+static void _reload_notify_handler(gpointer cache, gpointer user_data)
+#endif
+{
+    FmMenuVFileMonitor *mon = FM_MENU_VFILE_MONITOR(user_data);
+    GSList *items, *new_items, *ol, *nl;
+    MenuCacheItem *dir;
+    GFile *file;
+    const char *de_name;
+    guint32 de_flag;
+
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    if(mon->item == NULL) /* menu folder was destroyed or monitor cancelled */
+        return;
+    dir = mon->item;
+    if(mon->file->path)
+        mon->item = _vfile_path_to_menu_cache_item(cache, mon->file->path);
+    else
+        mon->item = MENU_CACHE_ITEM(menu_cache_dup_root_dir(cache));
+    if(mon->item && menu_cache_item_get_type(mon->item) != MENU_CACHE_TYPE_DIR)
+    {
+        menu_cache_item_unref(mon->item);
+        mon->item = NULL;
+    }
+    if(mon->item == NULL) /* folder was destroyed - emit event and exit */
+    {
+        menu_cache_item_unref(dir);
+        g_file_monitor_emit_event(G_FILE_MONITOR(mon), G_FILE(mon->file), NULL,
+                                  G_FILE_MONITOR_EVENT_DELETED);
+        return;
+    }
+    items = menu_cache_dir_list_children(MENU_CACHE_DIR(dir));
+    menu_cache_item_unref(dir);
+    new_items = menu_cache_dir_list_children(MENU_CACHE_DIR(mon->item));
+#else
+    if(mon->stopped) /* menu folder was destroyed or monitor cancelled */
+        return;
+    if(mon->file->path)
+        dir = _vfile_path_to_menu_cache_item(cache, mon->file->path);
+    else
+        dir = MENU_CACHE_ITEM(menu_cache_get_root_dir(cache));
+    if(dir == NULL) /* folder was destroyed - emit event and exit */
+    {
+        mon->stopped = TRUE;
+        g_file_monitor_emit_event(G_FILE_MONITOR(mon), G_FILE(mon->file), NULL,
+                                  G_FILE_MONITOR_EVENT_DELETED);
+        return;
+    }
+    /* emit change on the folder in any case */
+    g_file_monitor_emit_event(G_FILE_MONITOR(mon), G_FILE(mon->file), NULL,
+                              G_FILE_MONITOR_EVENT_CHANGED);
+    items = mon->items;
+    mon->items = g_slist_copy_deep(menu_cache_dir_get_children(MENU_CACHE_DIR(dir)),
+                                   (GCopyFunc)menu_cache_item_ref, NULL);
+    new_items = g_slist_copy_deep(mon->items, (GCopyFunc)menu_cache_item_ref, NULL);
+#endif
+    for (ol = items; ol; ) /* remove all separatorts first */
+    {
+        nl = ol->next;
+        if (menu_cache_item_get_id(ol->data) == NULL)
+        {
+            menu_cache_item_unref(ol->data);
+            items = g_slist_delete_link(items, ol);
+        }
+        ol = nl;
+    }
+    for (ol = new_items; ol; )
+    {
+        nl = ol->next;
+        if (menu_cache_item_get_id(ol->data) == NULL)
+        {
+            menu_cache_item_unref(ol->data);
+            new_items = g_slist_delete_link(new_items, ol);
+        }
+        ol = nl;
+    }
+    /* we have two copies of lists now, compare them and emit events */
+    ol = items;
+    de_name = g_getenv("XDG_CURRENT_DESKTOP");
+    if(de_name)
+        de_flag = menu_cache_get_desktop_env_flag(cache, de_name);
+    else
+        de_flag = (guint32)-1;
+    while (ol)
+    {
+        for (nl = new_items; nl; nl = nl->next)
+            if (strcmp(menu_cache_item_get_id(ol->data),
+                       menu_cache_item_get_id(nl->data)) == 0)
+                break; /* the same id found */
+        if (nl)
+        {
+            /* check if any visible attribute of it was changed */
+            if (g_strcmp0(menu_cache_item_get_name(ol->data),
+                          menu_cache_item_get_name(nl->data)) == 0 ||
+                g_strcmp0(menu_cache_item_get_icon(ol->data),
+                          menu_cache_item_get_icon(nl->data)) == 0 ||
+                menu_cache_app_get_is_visible(ol->data, de_flag) !=
+                                menu_cache_app_get_is_visible(nl->data, de_flag))
+            {
+                file = _fm_vfs_menu_resolve_relative_path(G_FILE(mon->file),
+                                             menu_cache_item_get_id(nl->data));
+                g_file_monitor_emit_event(G_FILE_MONITOR(mon), file, NULL,
+                                          G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED);
+                g_object_unref(file);
+            }
+            /* free both new and old from the list */
+            menu_cache_item_unref(nl->data);
+            new_items = g_slist_delete_link(new_items, nl);
+            nl = ol->next; /* use 'nl' as storage */
+            menu_cache_item_unref(ol->data);
+            items = g_slist_delete_link(items, ol);
+            ol = nl;
+        }
+        else /* id not found (removed), go to next */
+            ol = ol->next;
+    }
+    /* emit events for removed files */
+    while (items)
+    {
+        file = _fm_vfs_menu_resolve_relative_path(G_FILE(mon->file),
+                                             menu_cache_item_get_id(items->data));
+        g_file_monitor_emit_event(G_FILE_MONITOR(mon), file, NULL,
+                                  G_FILE_MONITOR_EVENT_DELETED);
+        g_object_unref(file);
+        menu_cache_item_unref(items->data);
+        items = g_slist_delete_link(items, items);
+    }
+    /* emit events for added files */
+    while (new_items)
+    {
+        file = _fm_vfs_menu_resolve_relative_path(G_FILE(mon->file),
+                                     menu_cache_item_get_id(new_items->data));
+        g_file_monitor_emit_event(G_FILE_MONITOR(mon), file, NULL,
+                                  G_FILE_MONITOR_EVENT_CREATED);
+        g_object_unref(file);
+        menu_cache_item_unref(new_items->data);
+        new_items = g_slist_delete_link(new_items, new_items);
+    }
+}
+
+static gboolean _fm_vfs_menu_monitor_dir_real(gpointer data)
+{
+    FmVfsMenuMainThreadData *init = data;
+    FmMenuVFileMonitor *mon;
+#if !MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    MenuCacheItem *dir;
+#endif
+
+    init->result = NULL;
+    if(g_cancellable_set_error_if_cancelled(init->cancellable, init->error))
+        return FALSE;
+    /* open menu cache instance */
+    mon = _fm_menu_vfile_monitor_new();
+    if(mon == NULL) /* out of memory! */
+        return FALSE;
+    mon->file = FM_MENU_VFILE(g_object_ref(init->destination));
+    mon->cache = _get_menu_cache(init->error);
+    if(mon->cache == NULL)
+        goto _fail;
+    /* check if requested path exists within cache */
+#if MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    if(mon->file->path)
+        mon->item = _vfile_path_to_menu_cache_item(mon->cache, mon->file->path);
+    else
+        mon->item = MENU_CACHE_ITEM(menu_cache_dup_root_dir(mon->cache));
+    if(mon->item == NULL || menu_cache_item_get_type(mon->item) != MENU_CACHE_TYPE_DIR)
+#else
+    if(mon->file->path)
+        dir = _vfile_path_to_menu_cache_item(mon->cache, mon->file->path);
+    else
+        dir = MENU_CACHE_ITEM(menu_cache_get_root_dir(mon->cache));
+    if(dir == NULL)
+#endif
+    {
+        g_set_error(init->error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                    _("FmMenuVFileMonitor: folder '%s' not found in menu cache"),
+                    mon->file->path);
+        goto _fail;
+    }
+#if !MENU_CACHE_CHECK_VERSION(0, 4, 0)
+    /* for old libmenu-cache we have no choice but copy all the data right now */
+    mon->items = g_slist_copy_deep(menu_cache_dir_get_children(MENU_CACHE_DIR(dir)),
+                                   (GCopyFunc)menu_cache_item_ref, NULL);
+#endif
+    if(g_cancellable_set_error_if_cancelled(init->cancellable, init->error))
+        goto _fail;
+    /* current directory contents belong to mon->item now */
+    /* attach reload notify handler */
+    mon->notifier = menu_cache_add_reload_notify(mon->cache,
+                                                 &_reload_notify_handler, mon);
+    init->result = mon;
+    return TRUE;
+
+_fail:
+    g_object_unref(mon);
+    return FALSE;
+}
+
+static GFileMonitor *_fm_vfs_menu_monitor_dir(GFile *file,
+                                              GFileMonitorFlags flags,
+                                              GCancellable *cancellable,
+                                              GError **error)
+{
+    FmVfsMenuMainThreadData enu;
+
+    /* g_debug("_fm_vfs_menu_monitor_dir %s", FM_MENU_VFILE(file)->path); */
+    enu.cancellable = cancellable;
+    enu.error = error;
+    // enu.flags = flags;
+    enu.destination = FM_MENU_VFILE(file);
+    RUN_WITH_MENU_CACHE(_fm_vfs_menu_monitor_dir_real, &enu);
+    return (GFileMonitor*)enu.result;
+}
+
+static GFileMonitor *_fm_vfs_menu_monitor_file(GFile *file,
+                                               GFileMonitorFlags flags,
+                                               GCancellable *cancellable,
+                                               GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+#if GLIB_CHECK_VERSION(2, 22, 0)
+static GFileIOStream *_fm_vfs_menu_open_readwrite(GFile *file,
+                                                  GCancellable *cancellable,
+                                                  GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static GFileIOStream *_fm_vfs_menu_create_readwrite(GFile *file,
+                                                    GFileCreateFlags flags,
+                                                    GCancellable *cancellable,
+                                                    GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static GFileIOStream *_fm_vfs_menu_replace_readwrite(GFile *file,
+                                                     const char *etag,
+                                                     gboolean make_backup,
+                                                     GFileCreateFlags flags,
+                                                     GCancellable *cancellable,
+                                                     GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+#endif /* Glib >= 2.22 */
+
+static void fm_menu_g_file_init(GFileIface *iface)
+{
+    GFileAttributeInfoList *list;
+
+    iface->dup = _fm_vfs_menu_dup;
+    iface->hash = _fm_vfs_menu_hash;
+    iface->equal = _fm_vfs_menu_equal;
+    iface->is_native = _fm_vfs_menu_is_native;
+    iface->has_uri_scheme = _fm_vfs_menu_has_uri_scheme;
+    iface->get_uri_scheme = _fm_vfs_menu_get_uri_scheme;
+    iface->get_basename = _fm_vfs_menu_get_basename;
+    iface->get_path = _fm_vfs_menu_get_path;
+    iface->get_uri = _fm_vfs_menu_get_uri;
+    iface->get_parse_name = _fm_vfs_menu_get_parse_name;
+    iface->get_parent = _fm_vfs_menu_get_parent;
+    iface->prefix_matches = _fm_vfs_menu_prefix_matches;
+    iface->get_relative_path = _fm_vfs_menu_get_relative_path;
+    iface->resolve_relative_path = _fm_vfs_menu_resolve_relative_path;
+    iface->get_child_for_display_name = _fm_vfs_menu_get_child_for_display_name;
+    iface->enumerate_children = _fm_vfs_menu_enumerate_children;
+    iface->query_info = _fm_vfs_menu_query_info;
+    iface->query_filesystem_info = _fm_vfs_menu_query_filesystem_info;
+    iface->find_enclosing_mount = _fm_vfs_menu_find_enclosing_mount;
+    iface->set_display_name = _fm_vfs_menu_set_display_name;
+    iface->query_settable_attributes = _fm_vfs_menu_query_settable_attributes;
+    iface->query_writable_namespaces = _fm_vfs_menu_query_writable_namespaces;
+    iface->set_attribute = _fm_vfs_menu_set_attribute;
+    iface->set_attributes_from_info = _fm_vfs_menu_set_attributes_from_info;
+    iface->read_fn = _fm_vfs_menu_read_fn;
+    iface->append_to = _fm_vfs_menu_append_to;
+    iface->create = _fm_vfs_menu_create;
+    iface->replace = _fm_vfs_menu_replace;
+    iface->delete_file = _fm_vfs_menu_delete_file;
+    iface->trash = _fm_vfs_menu_trash;
+    iface->make_directory = _fm_vfs_menu_make_directory;
+    iface->make_symbolic_link = _fm_vfs_menu_make_symbolic_link;
+    iface->copy = _fm_vfs_menu_copy;
+    iface->move = _fm_vfs_menu_move;
+    iface->monitor_dir = _fm_vfs_menu_monitor_dir;
+    iface->monitor_file = _fm_vfs_menu_monitor_file;
+#if GLIB_CHECK_VERSION(2, 22, 0)
+    iface->open_readwrite = _fm_vfs_menu_open_readwrite;
+    iface->create_readwrite = _fm_vfs_menu_create_readwrite;
+    iface->replace_readwrite = _fm_vfs_menu_replace_readwrite;
+    iface->supports_thread_contexts = TRUE;
+#endif /* Glib >= 2.22 */
+
+    list = g_file_attribute_info_list_new();
+    g_file_attribute_info_list_add(list, G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
+                                   G_FILE_ATTRIBUTE_TYPE_BOOLEAN,
+                                   G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
+    g_file_attribute_info_list_add(list, G_FILE_ATTRIBUTE_STANDARD_ICON,
+                                   G_FILE_ATTRIBUTE_TYPE_OBJECT,
+                                   G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
+    _fm_vfs_menu_settable_attributes = list;
+}
+
+
+/* ---- FmFile implementation ---- */
+static gboolean _fm_vfs_menu_wants_incremental(GFile* file)
+{
+    return FALSE;
+}
+
+static void fm_menu_fm_file_init(FmFileInterface *iface)
+{
+    iface->wants_incremental = _fm_vfs_menu_wants_incremental;
+}
+
+
+/* ---- interface for loading ---- */
+GFile *_fm_vfs_menu_new_for_uri(const char *uri)
+{
+    FmMenuVFile *item = _fm_menu_vfile_new();
+
+    if(uri == NULL)
+        uri = "";
+    /* skip menu:/ */
+    if(g_ascii_strncasecmp(uri, "menu:", 5) == 0)
+        uri += 5;
+    while(*uri == '/')
+        uri++;
+    /* skip "applications/" or "applications.menu/" */
+    if(g_ascii_strncasecmp(uri, "applications", 12) == 0)
+    {
+        uri += 12;
+        if(g_ascii_strncasecmp(uri, ".menu", 5) == 0)
+            uri += 5;
+    }
+    while(*uri == '/') /* skip starting slashes */
+        uri++;
+    /* save the rest of path, NULL means the root path */
+    if(*uri)
+    {
+        char *end;
+
+        item->path = g_strdup(uri);
+        for(end = item->path + strlen(item->path); end > item->path; end--)
+            if(end[-1] == '/') /* skip trailing slashes */
+                end[-1] = '\0';
+            else
+                break;
+    }
+    /* g_debug("_fm_vfs_menu_new_for_uri %s -> %s", uri, item->path); */
+    return (GFile*)item;
+}
diff --git a/src/core/vfs/vfs-search.c b/src/core/vfs/vfs-search.c
new file mode 100644 (file)
index 0000000..be5f3d7
--- /dev/null
@@ -0,0 +1,1335 @@
+/*
+ * fm-vfs-search.c
+ * 
+ * Copyright 2012 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ * Copyright 2010 Shae Smittle <starfall87@gmail.com>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ * 
+ * 
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "fm-file.h"
+
+#include <glib/gi18n-lib.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+#define _GNU_SOURCE /* for FNM_CASEFOLD in fnmatch.h, a GNU extension */
+#include <fnmatch.h>
+
+#if __GNUC__ >= 4
+#pragma GCC diagnostic ignored "-Wcomment" /* for comments below */
+#endif
+
+/* ---- Classes structures ---- */
+typedef struct _FmSearchIntIter FmSearchIntIter;
+
+struct _FmSearchIntIter
+{
+    FmSearchIntIter *parent; /* recursion path */
+    GFile *folder_path; /* path to folder */
+    GFileEnumerator *enu; /* children enumerator */
+};
+
+#define FM_TYPE_VFS_SEACRH_ENUMERATOR      (fm_vfs_search_enumerator_get_type())
+#define FM_VFS_SEACRH_ENUMERATOR(o)        (G_TYPE_CHECK_INSTANCE_CAST((o),\
+                            FM_TYPE_VFS_SEACRH_ENUMERATOR, FmVfsSearchEnumerator))
+
+typedef struct _FmVfsSearchEnumerator         FmVfsSearchEnumerator;
+typedef struct _FmVfsSearchEnumeratorClass    FmVfsSearchEnumeratorClass;
+
+struct _FmVfsSearchEnumerator
+{
+    GFileEnumerator parent;
+
+    FmSearchIntIter* iter;
+    char* attributes;
+    GFileQueryInfoFlags flags;
+    GSList* target_folders; /* GFile */
+    char** name_patterns;
+    GRegex* name_regex;
+    char* content_pattern;
+    GRegex* content_regex;
+    char** mime_types;
+    guint64 min_mtime;
+    guint64 max_mtime;
+    guint64 min_size;
+    guint64 max_size;
+    gboolean name_case_insensitive : 1;
+    gboolean content_case_insensitive : 1;
+    gboolean recursive : 1;
+    gboolean show_hidden : 1;
+};
+
+struct _FmVfsSearchEnumeratorClass
+{
+    GFileEnumeratorClass parent_class;
+};
+
+
+#define FM_TYPE_SEARCH_VFILE           (fm_vfs_search_file_get_type())
+#define FM_SEARCH_VFILE(o)             (G_TYPE_CHECK_INSTANCE_CAST((o), \
+                                        FM_TYPE_SEARCH_VFILE, FmSearchVFile))
+
+typedef struct _FmSearchVFile           FmSearchVFile;
+typedef struct _FmSearchVFileClass      FmSearchVFileClass;
+
+static GType fm_vfs_search_file_get_type (void);
+
+struct _FmSearchVFile
+{
+    GObject parent_object;
+
+    char *path; /* full search path */
+    GFile *current; /* last scanned directory */
+};
+
+struct _FmSearchVFileClass
+{
+  GObjectClass parent_class;
+};
+
+
+/* beforehand declarations */
+static gboolean fm_search_job_match_file(FmVfsSearchEnumerator * priv,
+                                         GFileInfo * info, GFile * parent,
+                                         GCancellable *cancellable,
+                                         GError **error);
+static void fm_search_job_match_folder(FmVfsSearchEnumerator * priv,
+                                       GFile * folder_path,
+                                       GCancellable *cancellable,
+                                       GError **error);
+static void parse_search_uri(FmVfsSearchEnumerator* priv, const char* uri_str);
+
+
+/* ---- Directory iterator ---- */
+/* caller should g_object_ref(folder_path) if success */
+static inline FmSearchIntIter *_search_iter_new(FmSearchIntIter *parent,
+                                                const char *attributes,
+                                                GFileQueryInfoFlags flags,
+                                                GFile *folder_path,
+                                                GCancellable *cancellable,
+                                                GError **error)
+{
+    GFileEnumerator *enu;
+    FmSearchIntIter *iter;
+
+    enu = g_file_enumerate_children(folder_path, attributes, flags,
+                                    cancellable, error);
+    if(enu == NULL)
+        return NULL;
+    iter = g_slice_new(FmSearchIntIter);
+    iter->parent = parent;
+    iter->folder_path = folder_path;
+    iter->enu = enu;
+    return iter;
+}
+
+static inline void _search_iter_free(FmSearchIntIter *iter, GCancellable *cancellable)
+{
+    g_file_enumerator_close(iter->enu, cancellable, NULL);
+    g_object_unref(iter->enu);
+    g_object_unref(iter->folder_path);
+    g_slice_free(FmSearchIntIter, iter);
+}
+
+
+/* ---- search enumerator class ---- */
+static GType fm_vfs_search_enumerator_get_type   (void);
+
+G_DEFINE_TYPE(FmVfsSearchEnumerator, fm_vfs_search_enumerator, G_TYPE_FILE_ENUMERATOR)
+
+static void _fm_vfs_search_enumerator_dispose(GObject *object)
+{
+    FmVfsSearchEnumerator *priv = FM_VFS_SEACRH_ENUMERATOR(object);
+    FmSearchIntIter *iter;
+
+    while((iter = priv->iter))
+    {
+        priv->iter = iter->parent;
+        _search_iter_free(iter, NULL);
+    }
+
+    if(priv->attributes)
+    {
+        g_free(priv->attributes);
+        priv->attributes = NULL;
+    }
+
+    if(priv->target_folders)
+    {
+        g_slist_foreach(priv->target_folders, (GFunc)g_object_unref, NULL);
+        g_slist_free(priv->target_folders);
+        priv->target_folders = NULL;
+    }
+
+    if(priv->name_patterns)
+    {
+        g_strfreev(priv->name_patterns);
+        priv->name_patterns = NULL;
+    }
+
+    if(priv->name_regex)
+    {
+        g_regex_unref(priv->name_regex);
+        priv->name_regex = NULL;
+    }
+
+    if(priv->content_pattern)
+    {
+        g_free(priv->content_pattern);
+        priv->content_pattern = NULL;
+    }
+
+    if(priv->content_regex)
+    {
+        g_regex_unref(priv->content_regex);
+        priv->content_regex = NULL;
+    }
+
+    if(priv->mime_types)
+    {
+        g_strfreev(priv->mime_types);
+        priv->mime_types = NULL;
+    }
+
+    G_OBJECT_CLASS(fm_vfs_search_enumerator_parent_class)->dispose(object);
+}
+
+static GFileInfo *_fm_vfs_search_enumerator_next_file(GFileEnumerator *enumerator,
+                                                      GCancellable *cancellable,
+                                                      GError **error)
+{
+    FmVfsSearchEnumerator *enu = FM_VFS_SEACRH_ENUMERATOR(enumerator);
+    FmSearchIntIter *iter;
+    GFileInfo * file_info;
+    GError *err = NULL;
+    FmSearchVFile *container;
+
+    /* g_debug("_fm_vfs_search_enumerator_next_file"); */
+    while(!g_cancellable_set_error_if_cancelled(cancellable, error))
+    {
+        iter = enu->iter;
+        if(iter == NULL) /* ended with folder */
+        {
+            if(enu->target_folders == NULL)
+                break;
+            iter = _search_iter_new(NULL, enu->attributes, enu->flags,
+                                    enu->target_folders->data,
+                                    cancellable, error);
+            if(iter == NULL)
+                break;
+            /* data is moved into iter now so free link itself */
+            enu->target_folders = g_slist_delete_link(enu->target_folders,
+                                                      enu->target_folders);
+            container = FM_SEARCH_VFILE(g_file_enumerator_get_container(enumerator));
+            if(container->current)
+                g_object_unref(container->current);
+            container->current = g_object_ref(iter->folder_path);
+            enu->iter = iter;
+        }
+
+        file_info = g_file_enumerator_next_file(iter->enu, cancellable, &err);
+        if(file_info && g_file_info_get_name(file_info))
+        {
+            /* check if directory itself matches criteria */
+            if(fm_search_job_match_file(enu, file_info, iter->folder_path,
+                                        cancellable, &err))
+            {
+                g_debug("found matched: %s", g_file_info_get_name(file_info));
+                return file_info;
+            }
+
+            /* recurse upon each directory */
+            if(err == NULL && enu->recursive &&
+               /* SF bug #969: very possibly we get multiple instances of the
+                  same file if we follow symlink to a directory
+                  FIXME: make it optional? */
+               !g_file_info_get_is_symlink(file_info) &&
+               g_file_info_get_file_type(file_info) == G_FILE_TYPE_DIRECTORY)
+            {
+                if(enu->show_hidden || !g_file_info_get_is_hidden(file_info))
+                {
+                    const char * name = g_file_info_get_name(file_info);
+                    GFile * file = g_file_get_child(iter->folder_path, name);
+                    /* go into directory and iterate it now */
+                    fm_search_job_match_folder(enu, file, cancellable, &err);
+                    g_object_unref(file);
+                }
+            }
+
+            g_object_unref(file_info);
+            if(err == NULL)
+                continue;
+        }
+
+        if(err != NULL) /* file_info == NULL */
+        {
+            if(err->domain == G_IO_ERROR && err->code == G_IO_ERROR_PERMISSION_DENIED)
+            {
+                g_error_free(err); /* ignore this error */
+                err = NULL;
+                continue;
+            }
+            g_propagate_error(error, err);
+            break;
+        }
+        /* else end of file list - go up */
+        enu->iter = iter->parent;
+        container = FM_SEARCH_VFILE(g_file_enumerator_get_container(enumerator));
+        if(container->current)
+            g_object_unref(container->current);
+        if(enu->iter)
+            container->current = g_object_ref(enu->iter->folder_path);
+        else
+            container->current = NULL;
+        _search_iter_free(iter, cancellable);
+    }
+    return NULL;
+}
+
+static gboolean _fm_vfs_search_enumerator_close(GFileEnumerator *enumerator,
+                                              GCancellable *cancellable,
+                                              GError **error)
+{
+    FmVfsSearchEnumerator *enu = FM_VFS_SEACRH_ENUMERATOR(enumerator);
+    FmSearchIntIter *iter;
+
+    while((iter = enu->iter))
+    {
+        enu->iter = iter->parent;
+        _search_iter_free(iter, cancellable);
+    }
+    return TRUE;
+}
+
+static void fm_vfs_search_enumerator_class_init(FmVfsSearchEnumeratorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+  GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS(klass);
+
+  gobject_class->dispose = _fm_vfs_search_enumerator_dispose;
+
+  enumerator_class->next_file = _fm_vfs_search_enumerator_next_file;
+  enumerator_class->close_fn = _fm_vfs_search_enumerator_close;
+  
+}
+
+static void fm_vfs_search_enumerator_init(FmVfsSearchEnumerator *enumerator)
+{
+    /* nothing */
+}
+
+static GFileEnumerator *_fm_vfs_search_enumerator_new(GFile *file,
+                                                      const char *path_str,
+                                                      const char *attributes,
+                                                      GFileQueryInfoFlags flags,
+                                                      GError **error)
+{
+    FmVfsSearchEnumerator *enumerator;
+
+    enumerator = g_object_new(FM_TYPE_VFS_SEACRH_ENUMERATOR, "container", file, NULL);
+
+    enumerator->attributes = g_strdup(attributes);
+    enumerator->flags = flags;
+    parse_search_uri(enumerator, path_str);
+    /* FIXME: don't ignore flags */
+
+    return G_FILE_ENUMERATOR(enumerator);
+}
+
+
+/* ---- The search engine ---- */
+
+/*
+ * name: parse_date_str
+ * @str: a string in YYYY-MM-DD format
+ * Return: a time_t value
+ */
+static time_t parse_date_str(const char* str)
+{
+    int len = strlen(str);
+    if(G_LIKELY(len >= 8))
+    {
+        struct tm timeinfo = {0};
+        if(sscanf(str, "%04d-%02d-%02d", &timeinfo.tm_year, &timeinfo.tm_mon, &timeinfo.tm_mday) == 3)
+        {
+            timeinfo.tm_year -= 1900; /* should be years since 1900 */
+            --timeinfo.tm_mon; /* month should be 0-11 */
+            return mktime(&timeinfo);
+        }
+    }
+    return 0;
+}
+
+/*
+ * parse_search_uri
+ * @job
+ * @uri: a search uri
+ * 
+ * Format of a search URI is similar to that of an http URI:
+ * 
+ * search://<folder1>,<folder2>,<folder...>?<parameter1=value1>&<parameter2=value2>&...
+ * The optional parameter key/value pairs are:
+ * show_hidden=<0 or 1>: whether to search for hidden files
+ * recursive=<0 or 1>: whether to search sub folders recursively
+ * name=<patterns>: patterns of filenames, separated by comma
+ * name_regex=<regular expression>: regular expression
+ * name_case_sensitive=<0 or 1>
+ * content=<content pattern>: search for files containing the pattern
+ * content_regex=<regular expression>: regular expression
+ * content_case_sensitive=<0 or 1>
+ * mime_types=<mime-types>: mime-types to search for, can use /* (ex: image/*), separated by ';'
+ * min_size=<bytes>
+ * max_size=<bytes>
+ * min_mtime=YYYY-MM-DD
+ * max_mtime=YYYY-MM-DD
+ * 
+ * An example to search all *.desktop files in /usr/share and /usr/local/share
+ * can be written like this:
+ * 
+ * search:///usr/share,/usr/local/share?recursive=1&show_hidden=0&name=*.desktop&name_ci=0
+ * 
+ * If the folder paths and parameters contain invalid characters for a
+ * URI, they should be escaped.
+ * 
+ */
+static void parse_search_uri(FmVfsSearchEnumerator* priv, const char* uri_str)
+{
+    const char scheme[] = "search://"; /* NOTE: sizeof(scheme) includes '\0' */
+    if(g_ascii_strncasecmp(uri_str, scheme, sizeof(scheme)-1) == 0)
+    {
+        const char* p = uri_str + sizeof(scheme)-1; /* skip scheme part */
+        char* params = strchr(p, '?');
+        char* name_regex = NULL;
+        char* content_regex = NULL;
+
+        /* add folder paths */
+        while (p)
+        {
+            char* sep = strchr(p, ','); /* use , to separate multiple paths */
+            char *path;
+
+            if (sep && (params == NULL || sep < params))
+                path = g_uri_unescape_segment(p, sep, NULL);
+            else if (params != NULL)
+            {
+                path = g_uri_unescape_segment(p, params, NULL);
+                sep = NULL;
+            }
+            else
+                path = g_uri_unescape_string(p, NULL);
+            /* g_debug("target folder path: %s", path); */
+            /* add the path to target folders */
+            priv->target_folders = g_slist_prepend(priv->target_folders,
+                                                   g_file_new_for_commandline_arg(path));
+            g_free(path);
+
+            p = sep;
+            if (p) /* it's on ':' now */
+                p++;
+        }
+
+        /* priv->target_folders = g_slist_reverse(priv->target_folders); */
+
+        /* decode parameters */
+        if(params)
+        {
+            params++; /* skip '?' */
+            while(*params)
+            {
+                /* parameters are in name=value pairs */
+                char *name;
+                char* value = strchr(params, '=');
+                char* sep = strchr(params, '&');
+
+                if (value && (sep == NULL || value < sep))
+                {
+                    name = g_strndup(params, value - params);
+                    if (sep)
+                        value = g_uri_unescape_segment(value+1, sep, NULL);
+                    else
+                        value = g_uri_unescape_string(value+1, NULL);
+                }
+                else if (sep)
+                {
+                    name = g_strndup(params, sep - params);
+                    value = NULL;
+                }
+                else /* value == NULL && sep == NULL */
+                    name = g_strdup(params);
+
+                /* g_printf("parameter name/value: %s = %s\n", name, value); */
+
+                if(strcmp(name, "show_hidden") == 0)
+                    priv->show_hidden = (value[0] == '1') ? TRUE : FALSE;
+                else if(strcmp(name, "recursive") == 0)
+                    priv->recursive = (value[0] == '1') ? TRUE : FALSE;
+                else if(strcmp(name, "name") == 0)
+                    priv->name_patterns = g_strsplit(value, ",", 0);
+                else if(strcmp(name, "name_regex") == 0)
+                {
+                    g_free(name_regex);
+                    name_regex = value;
+                    value = NULL;
+                }
+                else if(strcmp(name, "name_ci") == 0)
+                    priv->name_case_insensitive = (value[0] == '1') ? TRUE : FALSE;
+                else if(strcmp(name, "content") == 0)
+                {
+                    g_free(priv->content_pattern);
+                    priv->content_pattern = value;
+                    value = NULL;
+                }
+                else if(strcmp(name, "content_regex") == 0)
+                {
+                    g_free(content_regex);
+                    content_regex = value;
+                    value = NULL;
+                }
+                else if(strcmp(name, "content_ci") == 0)
+                    priv->content_case_insensitive = (value[0] == '1') ? TRUE : FALSE;
+                else if(strcmp(name, "mime_types") == 0)
+                {
+                    priv->mime_types = g_strsplit(value, ";", -1);
+
+                    /* For mime_type patterns such as image/* and audio/*,
+                     * we move the trailing '*' to begining of the string
+                     * as a measure of optimization. Later we can detect if it's a
+                     * pattern or a full type name by checking the first char. */
+                    if(priv->mime_types)
+                    {
+                        char** pmime_type;
+                        for(pmime_type = priv->mime_types; *pmime_type; ++pmime_type)
+                        {
+                            char* mime_type = *pmime_type;
+                            int len = strlen(mime_type);
+                            /* if the mime_type is end with "/*" */
+                            if(len > 2 && /*mime_type[len - 2] == '/' &&*/ mime_type[len - 1] == '*')
+                            {
+                                /* move the trailing * to first char */
+                                memmove(mime_type + 1, mime_type, len - 1);
+                                mime_type[0] = '*';
+                            }
+                        }
+
+                        if (!g_strstr_len(priv->attributes, -1, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE))
+                        {
+                            gchar * attributes = g_strconcat(priv->attributes, ",", G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, NULL);
+                            g_free(priv->attributes);
+                            priv->attributes = attributes;
+                        }
+
+                    }
+                }
+                else if(strcmp(name, "min_size") == 0)
+                    priv->min_size = atoll(value);
+                else if(strcmp(name, "max_size") == 0)
+                    priv->max_size = atoll(value);
+                else if(strcmp(name, "min_mtime") == 0)
+                    priv->min_mtime = (guint64)parse_date_str(value);
+                else if(strcmp(name, "max_mtime") == 0)
+                    priv->max_mtime = (guint64)parse_date_str(value);
+
+                g_free(name);
+                g_free(value);
+
+                /* continue with the next param=value pair */
+                if(sep)
+                    params = sep + 1;
+                else
+                    break;
+            }
+
+            if(name_regex)
+            {
+                /* we set G_REGEX_RAW because GLib might cause a crash
+                   if a search is done in a non-utf8 string */
+                GRegexCompileFlags flags = G_REGEX_RAW;
+                if(priv->name_case_insensitive)
+                    flags |= G_REGEX_CASELESS;
+                priv->name_regex = g_regex_new(name_regex, flags, 0, NULL);
+                g_free(name_regex);
+            }
+
+            if(content_regex)
+            {
+                GRegexCompileFlags flags = G_REGEX_RAW; /* like above */
+                if(priv->content_case_insensitive)
+                    flags |= G_REGEX_CASELESS;
+                priv->content_regex = g_regex_new(content_regex, flags, 0, NULL);
+                g_free(content_regex);
+            }
+
+            if(priv->content_case_insensitive) /* case insensitive */
+            {
+                if(priv->content_pattern) /* make sure the pattern is lower case */
+                {
+                    char* down = g_utf8_strdown(priv->content_pattern, -1);
+                    g_free(priv->content_pattern);
+                    priv->content_pattern = down;
+                }
+            }
+        }
+    }
+}
+
+static void fm_search_job_match_folder(FmVfsSearchEnumerator * priv,
+                                       GFile * folder_path,
+                                       GCancellable *cancellable,
+                                       GError **error)
+{
+    FmSearchIntIter *iter;
+    FmSearchVFile *container;
+
+    /* FIXME: make error if NULL */
+    iter = _search_iter_new(priv->iter, priv->attributes, priv->flags, folder_path,
+                            cancellable, error);
+    if(iter == NULL) /* error */
+        return;
+    g_object_ref(folder_path); /* it's copied into iter */
+    priv->iter = iter;
+    container = FM_SEARCH_VFILE(g_file_enumerator_get_container(G_FILE_ENUMERATOR(priv)));
+    if(container->current)
+        g_object_unref(container->current);
+    container->current = g_object_ref(folder_path);
+}
+
+static gboolean fm_search_job_match_filename(FmVfsSearchEnumerator* priv, GFileInfo* info)
+{
+    gboolean ret;
+
+    /* g_debug("fm_search_job_match_filename: %s", g_file_info_get_name(info)); */
+    if(priv->name_regex)
+    {
+        const char* name = g_file_info_get_name(info);
+        ret = g_regex_match(priv->name_regex, name, 0, NULL);
+    }
+    else if(priv->name_patterns)
+    {
+        ret = FALSE;
+        const char* name = g_file_info_get_name(info);
+        char** ppattern;
+        for(ppattern = priv->name_patterns; *ppattern; ++ppattern)
+        {
+            const char* pattern = *ppattern;
+            /* FIXME: FNM_CASEFOLD is a GNU extension */
+            int flags = FNM_PERIOD;
+            if(priv->name_case_insensitive)
+                flags |= FNM_CASEFOLD;
+            if(fnmatch(pattern, name, flags) == 0)
+                ret = TRUE;
+        }
+    }
+    else
+        ret = TRUE;
+    return ret;
+}
+
+static gboolean fm_search_job_match_content_line_based(FmVfsSearchEnumerator* priv,
+                                                       GFileInfo* info,
+                                                       GInputStream* stream,
+                                                       GCancellable* cancellable,
+                                                       GError** error)
+{
+    gboolean ret = FALSE;
+    /* create a buffered data input stream for line-based I/O */
+    GDataInputStream *input_stream = g_data_input_stream_new(stream);
+    do
+    {
+        gsize line_len;
+        char* line = g_data_input_stream_read_line(input_stream, &line_len, cancellable, error);
+        if(line == NULL) /* error or EOF */
+            break;
+        if(priv->content_regex)
+        {
+            /* match using regexp */
+            ret = g_regex_match(priv->content_regex, line, 0, NULL);
+        }
+        else if(priv->content_pattern && priv->content_case_insensitive)
+        {
+            /* case insensitive search is line-based because we need to
+             * do utf8 validation + case conversion and it's easier to
+             * do with lines than with raw streams. */
+            if(g_utf8_validate(line, -1, NULL))
+            {
+                /* this whole line contains valid UTF-8 */
+                char* down = g_utf8_strdown(line, -1);
+                g_free(line);
+                line = down;
+            }
+            else /* non-UTF8, treat as ASCII */
+            {
+                char* p;
+                for(p = line; *p; ++p)
+                    *p = g_ascii_tolower(*p);
+            }
+
+            if(strstr(line, priv->content_pattern))
+                ret = TRUE;
+        }
+        g_free(line);
+    }while(ret == FALSE);
+    g_object_unref(input_stream);
+    return ret;
+}
+
+static gboolean fm_search_job_match_content_exact(FmVfsSearchEnumerator* priv,
+                                                  GFileInfo* info,
+                                                  GInputStream* stream,
+                                                  GCancellable* cancellable,
+                                                  GError** error)
+{
+    gboolean ret = FALSE;
+    char *buf, *pbuf;
+    gssize size;
+
+    /* Ensure that the allocated buffer is longer than the string being
+     * searched for. Otherwise it's not possible for the buffer to 
+     * contain a string fully matching the pattern. */
+    int pattern_len = strlen(priv->content_pattern);
+    int buf_size = pattern_len > 4095 ? pattern_len : 4095;
+    int bytes_to_read;
+
+    buf = g_new(char, buf_size + 1); /* +1 for terminating null char. */
+    bytes_to_read = buf_size;
+    pbuf = buf;
+    for(;;)
+    {
+        char* found;
+        size = g_input_stream_read(stream, pbuf, bytes_to_read, cancellable, error);
+        if(size <=0) /* EOF or error */
+            break;
+        pbuf[size] = '\0'; /* make the string null terminated */
+
+        found = strstr(buf, priv->content_pattern);
+        if(found) /* the string is found in the buffer */
+        {
+            ret = TRUE;
+            break;
+        }
+        else if(size == bytes_to_read) /* if size < bytes_to_read, we're at EOF and there are no further data. */
+        {
+            /* Preserve the last <pattern_len-1> bytes and move them to 
+             * the beginning of the buffer.
+             * Append further data after this chunk of data at next read. */
+            int preserve_len = pattern_len - 1;
+            char* buf_end = buf + buf_size;
+            memmove(buf, buf_end - preserve_len, preserve_len);
+            pbuf = buf + preserve_len;
+            bytes_to_read = buf_size - preserve_len;
+        }
+    }
+    g_free(buf);
+    return ret;
+}
+
+static gboolean fm_search_job_match_content(FmVfsSearchEnumerator* priv,
+                                            GFileInfo* info, GFile* parent,
+                                            GCancellable* cancellable,
+                                            GError** error)
+{
+    gboolean ret;
+    if(priv->content_pattern || priv->content_regex)
+    {
+        ret = FALSE;
+        if(g_file_info_get_file_type(info) == G_FILE_TYPE_REGULAR && g_file_info_get_size(info) > 0)
+        {
+            GFile* file = g_file_get_child(parent, g_file_info_get_name(info));
+            /* NOTE: I disabled mmap-based search since this could cause
+             * unexpected crashes sometimes if the mapped files are
+             * removed or changed during the search. */
+            GFileInputStream * stream = g_file_read(file, cancellable, error);
+            g_object_unref(file);
+
+            if(stream)
+            {
+                if(priv->content_pattern && !priv->content_case_insensitive)
+                {
+                    /* stream based search optimized for case sensitive
+                     * exact match. */
+                    ret = fm_search_job_match_content_exact(priv, info,
+                                                        G_INPUT_STREAM(stream),
+                                                        cancellable, error);
+                }
+                else
+                {
+                    /* grep-like regexp search and case insensitive search
+                     * are line-based. */
+                    ret = fm_search_job_match_content_line_based(priv, info,
+                                                        G_INPUT_STREAM(stream),
+                                                        cancellable, error);
+                }
+
+                g_input_stream_close(G_INPUT_STREAM(stream), cancellable, NULL);
+                g_object_unref(stream);
+            }
+        }
+    }
+    else
+        ret = TRUE;
+    return ret;
+}
+
+static gboolean fm_search_job_match_file_type(FmVfsSearchEnumerator* priv, GFileInfo* info)
+{
+    gboolean ret;
+    if(priv->mime_types)
+    {
+        const char* file_type = g_file_info_get_content_type(info);
+        char** pmime_type;
+        ret = FALSE;
+        for(pmime_type = priv->mime_types; *pmime_type; ++pmime_type)
+        {
+            const char* mime_type = *pmime_type;
+            /* For mime_type patterns such as image/* and audio/*,
+             * we move the trailing '*' to begining of the string
+             * as a measure of optimization. We can know it's a
+             * pattern not a full type name by checking the first char. */
+            if(mime_type[0] == '*')
+            {
+                if(g_str_has_prefix(file_type, mime_type + 1))
+                {
+                    ret = TRUE;
+                    break;
+                }
+            }
+            else if(g_content_type_is_a(file_type, mime_type))
+            {
+                ret = TRUE;
+                break;
+            }
+        }
+    }
+    else
+        ret = TRUE;
+    return ret;
+}
+
+static gboolean fm_search_job_match_size(FmVfsSearchEnumerator* priv, GFileInfo* info)
+{
+    guint64 size = g_file_info_get_size(info);
+    gboolean ret = TRUE;
+    if(priv->min_size > 0 && size < priv->min_size)
+        ret = FALSE;
+    else if(priv->max_size > 0 && size > priv->max_size)
+        ret = FALSE;
+    else if ((priv->min_size > 0 || priv->max_size > 0) && g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY)
+        ret = FALSE;
+    return ret;
+}
+
+static gboolean fm_search_job_match_mtime(FmVfsSearchEnumerator* priv, GFileInfo* info)
+{
+    gboolean ret = TRUE;
+    if(priv->min_mtime || priv->max_mtime)
+    {
+        guint64 mtime = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+        /* g_print("file mtime: %llu, min_mtime=%llu, max_mtime=%llu\n", mtime, priv->min_mtime, priv->max_mtime); */
+        if(priv->min_mtime > 0 && mtime < priv->min_mtime)
+            ret = FALSE; /* earlier than min_mtime */
+        else if(priv->max_mtime > 0 && mtime > priv->max_mtime)
+            ret = FALSE; /* later than max_mtime */
+    }
+    return ret;
+}
+
+static gboolean fm_search_job_match_file(FmVfsSearchEnumerator * priv,
+                                         GFileInfo * info, GFile * parent,
+                                         GCancellable *cancellable,
+                                         GError **error)
+{
+    //g_print("matching file %s\n", g_file_info_get_name(info));
+
+    if(!priv->show_hidden && g_file_info_get_is_hidden(info))
+        return FALSE;
+
+    if(!fm_search_job_match_filename(priv, info))
+        return FALSE;
+
+    if(!fm_search_job_match_file_type(priv, info))
+        return FALSE;
+
+    if(!fm_search_job_match_size(priv, info))
+        return FALSE;
+
+    if(!fm_search_job_match_mtime(priv, info))
+        return FALSE;
+
+    if(!fm_search_job_match_content(priv, info, parent, cancellable, error))
+        return FALSE;
+
+    return TRUE;
+}
+
+
+/* end of rule functions */
+
+/* ---- FmSearchVFile class ---- */
+static void fm_search_g_file_init(GFileIface *iface);
+static void fm_search_fm_file_init(FmFileInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE(FmSearchVFile, fm_vfs_search_file, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE(G_TYPE_FILE, fm_search_g_file_init)
+                        G_IMPLEMENT_INTERFACE(FM_TYPE_FILE, fm_search_fm_file_init))
+
+static void fm_vfs_search_file_finalize(GObject *object)
+{
+    FmSearchVFile *item = FM_SEARCH_VFILE(object);
+
+    g_free(item->path);
+    if(item->current)
+        g_object_unref(item->current);
+
+    G_OBJECT_CLASS(fm_vfs_search_file_parent_class)->finalize(object);
+}
+
+static void fm_vfs_search_file_class_init(FmSearchVFileClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+    gobject_class->finalize = fm_vfs_search_file_finalize;
+}
+
+static void fm_vfs_search_file_init(FmSearchVFile *item)
+{
+    /* nothing */
+}
+
+static FmSearchVFile *_fm_search_vfile_new(void)
+{
+    return (FmSearchVFile*)g_object_new(FM_TYPE_SEARCH_VFILE, NULL);
+}
+
+
+/* ---- GFile implementation ---- */
+#define ERROR_UNSUPPORTED(err) g_set_error_literal(err, G_IO_ERROR, \
+                        G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"))
+
+static GFile *_fm_vfs_search_dup(GFile *file)
+{
+    FmSearchVFile *item, *new_item;
+
+    item = FM_SEARCH_VFILE(file);
+    new_item = _fm_search_vfile_new();
+    if(item->path)
+        new_item->path = g_strdup(item->path);
+    return (GFile*)new_item;
+}
+
+static guint _fm_vfs_search_hash(GFile *file)
+{
+    return g_str_hash(FM_SEARCH_VFILE(file)->path);
+}
+
+static gboolean _fm_vfs_search_equal(GFile *file1, GFile *file2)
+{
+    char *path1 = FM_SEARCH_VFILE(file1)->path;
+    char *path2 = FM_SEARCH_VFILE(file2)->path;
+
+    return g_str_equal(path1, path2);
+}
+
+static gboolean _fm_vfs_search_is_native(GFile *file)
+{
+    return FALSE;
+}
+
+static gboolean _fm_vfs_search_has_uri_scheme(GFile *file, const char *uri_scheme)
+{
+    return g_ascii_strcasecmp(uri_scheme, "search") == 0;
+}
+
+static char *_fm_vfs_search_get_uri_scheme(GFile *file)
+{
+    return g_strdup("search");
+}
+
+static char *_fm_vfs_search_get_basename(GFile *file)
+{
+    return g_strdup("/");
+}
+
+static char *_fm_vfs_search_get_path(GFile *file)
+{
+    return NULL;
+}
+
+static char *_fm_vfs_search_get_uri(GFile *file)
+{
+    FmSearchVFile *item = FM_SEARCH_VFILE(file);
+
+    if(item->current)
+        return g_file_get_uri(item->current);
+    return g_strdup(item->path);
+}
+
+static char *_fm_vfs_search_get_parse_name(GFile *file)
+{
+    /* FIXME: need name to be converted to UTF-8? */
+    return g_strdup(_("Search"));
+}
+
+static GFile *_fm_vfs_search_get_parent(GFile *file)
+{
+    return NULL;
+}
+
+static gboolean _fm_vfs_search_prefix_matches(GFile *prefix, GFile *file)
+{
+    return FALSE;
+}
+
+static char *_fm_vfs_search_get_relative_path(GFile *parent, GFile *descendant)
+{
+    return NULL;
+}
+
+static GFile *_fm_vfs_search_resolve_relative_path(GFile *file, const char *relative_path)
+{
+    return NULL;
+}
+
+static GFile *_fm_vfs_search_get_child_for_display_name(GFile *file,
+                                                        const char *display_name,
+                                                        GError **error)
+{
+    FmSearchVFile *new_item;
+
+    g_return_val_if_fail(file != NULL, NULL);
+
+    if (display_name == NULL || *display_name == '\0')
+        return g_object_ref(file);
+    /* just append "/"display_name to file->path */
+    new_item = _fm_search_vfile_new();
+    new_item->path = g_strdup_printf("%s/%s", FM_SEARCH_VFILE(file)->path, display_name);
+    return (GFile*)new_item;
+}
+
+static GFileEnumerator *_fm_vfs_search_enumerate_children(GFile *file,
+                                                          const char *attributes,
+                                                          GFileQueryInfoFlags flags,
+                                                          GCancellable *cancellable,
+                                                          GError **error)
+{
+    const char *path = FM_SEARCH_VFILE(file)->path;
+
+    return _fm_vfs_search_enumerator_new(file, path, attributes, flags, error);
+}
+
+static GFileInfo *_fm_vfs_search_query_info(GFile *file,
+                                            const char *attributes,
+                                            GFileQueryInfoFlags flags,
+                                            GCancellable *cancellable,
+                                            GError **error)
+{
+    GFileInfo *fileinfo = g_file_info_new();
+    GIcon* icon;
+
+    /* g_debug("_fm_vfs_search_query_info on %s", FM_SEARCH_VFILE(file)->path); */
+    /* FIXME: use matcher to set only requested data */
+    g_file_info_set_name(fileinfo, FM_SEARCH_VFILE(file)->path);
+    g_file_info_set_display_name(fileinfo, _("Search Results"));
+    icon = g_themed_icon_new("search");
+    g_file_info_set_icon(fileinfo, icon);
+    g_object_unref(icon);
+    g_file_info_set_file_type(fileinfo, G_FILE_TYPE_DIRECTORY);
+    return fileinfo;
+}
+
+static GFileInfo *_fm_vfs_search_query_filesystem_info(GFile *file,
+                                                       const char *attributes,
+                                                       GCancellable *cancellable,
+                                                       GError **error)
+{
+    /* FIXME: set some info on it */
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static GMount *_fm_vfs_search_find_enclosing_mount(GFile *file,
+                                                   GCancellable *cancellable,
+                                                   GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static GFile *_fm_vfs_search_set_display_name(GFile *file,
+                                              const char *display_name,
+                                              GCancellable *cancellable,
+                                              GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static GFileAttributeInfoList *_fm_vfs_search_query_settable_attributes(GFile *file,
+                                                                        GCancellable *cancellable,
+                                                                        GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static GFileAttributeInfoList *_fm_vfs_search_query_writable_namespaces(GFile *file,
+                                                                        GCancellable *cancellable,
+                                                                        GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static gboolean _fm_vfs_search_set_attribute(GFile *file,
+                                             const char *attribute,
+                                             GFileAttributeType type,
+                                             gpointer value_p,
+                                             GFileQueryInfoFlags flags,
+                                             GCancellable *cancellable,
+                                             GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return FALSE;
+}
+
+static gboolean _fm_vfs_search_set_attributes_from_info(GFile *file,
+                                                        GFileInfo *info,
+                                                        GFileQueryInfoFlags flags,
+                                                        GCancellable *cancellable,
+                                                        GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return FALSE;
+}
+
+static GFileInputStream *_fm_vfs_search_read_fn(GFile *file,
+                                                GCancellable *cancellable,
+                                                GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static GFileOutputStream *_fm_vfs_search_append_to(GFile *file,
+                                                   GFileCreateFlags flags,
+                                                   GCancellable *cancellable,
+                                                   GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static GFileOutputStream *_fm_vfs_search_create(GFile *file,
+                                                GFileCreateFlags flags,
+                                                GCancellable *cancellable,
+                                                GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static GFileOutputStream *_fm_vfs_search_replace(GFile *file,
+                                                 const char *etag,
+                                                 gboolean make_backup,
+                                                 GFileCreateFlags flags,
+                                                 GCancellable *cancellable,
+                                                 GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static gboolean _fm_vfs_search_delete_file(GFile *file,
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return FALSE;
+}
+
+static gboolean _fm_vfs_search_trash(GFile *file,
+                                     GCancellable *cancellable,
+                                     GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return FALSE;
+}
+
+static gboolean _fm_vfs_search_make_directory(GFile *file,
+                                              GCancellable *cancellable,
+                                              GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return FALSE;
+}
+
+static gboolean _fm_vfs_search_make_symbolic_link(GFile *file,
+                                                  const char *symlink_value,
+                                                  GCancellable *cancellable,
+                                                  GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return FALSE;
+}
+
+static gboolean _fm_vfs_search_copy(GFile *source,
+                                    GFile *destination,
+                                    GFileCopyFlags flags,
+                                    GCancellable *cancellable,
+                                    GFileProgressCallback progress_callback,
+                                    gpointer progress_callback_data,
+                                    GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return FALSE;
+}
+
+static gboolean _fm_vfs_search_move(GFile *source,
+                                    GFile *destination,
+                                    GFileCopyFlags flags,
+                                    GCancellable *cancellable,
+                                    GFileProgressCallback progress_callback,
+                                    gpointer progress_callback_data,
+                                    GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return FALSE;
+}
+
+static GFileMonitor *_fm_vfs_search_monitor_dir(GFile *file,
+                                                GFileMonitorFlags flags,
+                                                GCancellable *cancellable,
+                                                GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static GFileMonitor *_fm_vfs_search_monitor_file(GFile *file,
+                                                 GFileMonitorFlags flags,
+                                                 GCancellable *cancellable,
+                                                 GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+#if GLIB_CHECK_VERSION(2, 22, 0)
+static GFileIOStream *_fm_vfs_search_open_readwrite(GFile *file,
+                                                    GCancellable *cancellable,
+                                                    GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static GFileIOStream *_fm_vfs_search_create_readwrite(GFile *file,
+                                                      GFileCreateFlags flags,
+                                                      GCancellable *cancellable,
+                                                      GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+
+static GFileIOStream *_fm_vfs_search_replace_readwrite(GFile *file,
+                                                       const char *etag,
+                                                       gboolean make_backup,
+                                                       GFileCreateFlags flags,
+                                                       GCancellable *cancellable,
+                                                       GError **error)
+{
+    ERROR_UNSUPPORTED(error);
+    return NULL;
+}
+#endif /* Glib >= 2.22 */
+
+static void fm_search_g_file_init(GFileIface *iface)
+{
+    iface->dup = _fm_vfs_search_dup;
+    iface->hash = _fm_vfs_search_hash;
+    iface->equal = _fm_vfs_search_equal;
+    iface->is_native = _fm_vfs_search_is_native;
+    iface->has_uri_scheme = _fm_vfs_search_has_uri_scheme;
+    iface->get_uri_scheme = _fm_vfs_search_get_uri_scheme;
+    iface->get_basename = _fm_vfs_search_get_basename;
+    iface->get_path = _fm_vfs_search_get_path;
+    iface->get_uri = _fm_vfs_search_get_uri;
+    iface->get_parse_name = _fm_vfs_search_get_parse_name;
+    iface->get_parent = _fm_vfs_search_get_parent;
+    iface->prefix_matches = _fm_vfs_search_prefix_matches;
+    iface->get_relative_path = _fm_vfs_search_get_relative_path;
+    iface->resolve_relative_path = _fm_vfs_search_resolve_relative_path;
+    iface->get_child_for_display_name = _fm_vfs_search_get_child_for_display_name;
+    iface->enumerate_children = _fm_vfs_search_enumerate_children;
+    iface->query_info = _fm_vfs_search_query_info;
+    iface->query_filesystem_info = _fm_vfs_search_query_filesystem_info;
+    iface->find_enclosing_mount = _fm_vfs_search_find_enclosing_mount;
+    iface->set_display_name = _fm_vfs_search_set_display_name;
+    iface->query_settable_attributes = _fm_vfs_search_query_settable_attributes;
+    iface->query_writable_namespaces = _fm_vfs_search_query_writable_namespaces;
+    iface->set_attribute = _fm_vfs_search_set_attribute;
+    iface->set_attributes_from_info = _fm_vfs_search_set_attributes_from_info;
+    iface->read_fn = _fm_vfs_search_read_fn;
+    iface->append_to = _fm_vfs_search_append_to;
+    iface->create = _fm_vfs_search_create;
+    iface->replace = _fm_vfs_search_replace;
+    iface->delete_file = _fm_vfs_search_delete_file;
+    iface->trash = _fm_vfs_search_trash;
+    iface->make_directory = _fm_vfs_search_make_directory;
+    iface->make_symbolic_link = _fm_vfs_search_make_symbolic_link;
+    iface->copy = _fm_vfs_search_copy;
+    iface->move = _fm_vfs_search_move;
+    iface->monitor_dir = _fm_vfs_search_monitor_dir;
+    iface->monitor_file = _fm_vfs_search_monitor_file;
+#if GLIB_CHECK_VERSION(2, 22, 0)
+    iface->open_readwrite = _fm_vfs_search_open_readwrite;
+    iface->create_readwrite = _fm_vfs_search_create_readwrite;
+    iface->replace_readwrite = _fm_vfs_search_replace_readwrite;
+    iface->supports_thread_contexts = TRUE;
+#endif /* Glib >= 2.22 */
+}
+
+
+/* ---- FmFile implementation ---- */
+static gboolean _fm_vfs_search_wants_incremental(GFile* file)
+{
+    return TRUE;
+}
+
+static void fm_search_fm_file_init(FmFileInterface *iface)
+{
+    iface->wants_incremental = _fm_vfs_search_wants_incremental;
+}
+
+
+/* ---- interface for loading ---- */
+GFile *_fm_vfs_search_new_for_uri(const char *uri)
+{
+    FmSearchVFile *item;
+    g_return_val_if_fail(uri != NULL, NULL);
+    item = _fm_search_vfile_new();
+    item->path = g_strdup(uri);
+    return (GFile*)item;
+}
diff --git a/src/core/volumemanager.cpp b/src/core/volumemanager.cpp
new file mode 100644 (file)
index 0000000..522c38c
--- /dev/null
@@ -0,0 +1,111 @@
+#include "volumemanager.h"
+
+namespace Fm {
+
+std::mutex VolumeManager::mutex_;
+std::weak_ptr<VolumeManager> 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> VolumeManager::globalInstance() {
+    std::lock_guard<std::mutex> lock{mutex_};
+    auto mon = globalInstance_.lock();
+    if(mon == nullptr) {
+        mon = std::make_shared<VolumeManager>();
+        globalInstance_ = mon;
+    }
+    return mon;
+}
+
+void VolumeManager::onGetGVolumeMonitorFinished() {
+    auto job = static_cast<GetGVolumeMonitorJob*>(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
new file mode 100644 (file)
index 0000000..642adc8
--- /dev/null
@@ -0,0 +1,237 @@
+#ifndef FM2_VOLUMEMANAGER_H
+#define FM2_VOLUMEMANAGER_H
+
+#include "../libfmqtglobals.h"
+#include <QObject>
+#include <gio/gio.h>
+#include "gioptrs.h"
+#include "filepath.h"
+#include "iconinfo.h"
+#include "job.h"
+#include <vector>
+#include <mutex>
+
+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<const IconInfo> 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<const IconInfo> 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<Volume>& volumes() const {
+        return volumes_;
+    }
+
+    const std::vector<Mount>& mounts() const {
+        return mounts_;
+    }
+
+    static std::shared_ptr<VolumeManager> 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<Volume> volumes_;
+    std::vector<Mount> mounts_;
+
+    static std::mutex mutex_;
+    static std::weak_ptr<VolumeManager> globalInstance_;
+};
+
+} // namespace Fm
+
+#endif // FM2_VOLUMEMANAGER_H
diff --git a/src/createnewmenu.cpp b/src/createnewmenu.cpp
new file mode 100644 (file)
index 0000000..06ca36d
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "createnewmenu.h"
+#include "folderview.h"
+#include "utilities.h"
+#include "core/iconinfo.h"
+#include "core/templates.h"
+
+#include <algorithm>
+
+namespace Fm {
+
+
+class TemplateAction: public QAction {
+public:
+    TemplateAction(std::shared_ptr<const TemplateItem> item, QObject* parent):
+        QAction(parent) {
+        setTemplateItem(std::move(item));
+    }
+
+    const std::shared_ptr<const TemplateItem> templateItem() const {
+        return templateItem_;
+    }
+
+    void setTemplateItem(std::shared_ptr<const TemplateItem> item) {
+        templateItem_ = std::move(item);
+        auto mimeType = templateItem_->mimeType();
+        setText(QString("%1 (%2)").arg(templateItem_->displayName()).arg(mimeType->desc()));
+        setIcon(templateItem_->icon()->qicon());
+    }
+
+private:
+    std::shared_ptr<const TemplateItem> templateItem_;
+};
+
+
+CreateNewMenu::CreateNewMenu(QWidget* dialogParent, Fm::FilePath dirPath, QWidget* parent):
+    QMenu(parent),
+    dialogParent_(dialogParent),
+    dirPath_(std::move(dirPath)),
+    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);
+    // when a template directory is already loaded
+    templates_->forEachItem([this](const std::shared_ptr<const TemplateItem>& 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<TemplateAction*>(sender());
+    if(dirPath_) {
+        createFileOrFolder(CreateWithTemplate, dirPath_, action->templateItem().get(), dialogParent_);
+    }
+}
+
+void CreateNewMenu::addTemplateItem(const std::shared_ptr<const TemplateItem> &item) {
+    if(!templateSeparator_) {
+        templateSeparator_= addSeparator();
+    }
+    auto mimeType = item->mimeType();
+    /* we support directories differently */
+    if(mimeType->isDir()) {
+        return;
+    }
+
+    QAction* action = new TemplateAction{item, this};
+    connect(action, &QAction::triggered, this, &CreateNewMenu::onCreateNew);
+
+    // sort actions alphabetically
+    const auto allActions = actions();
+    auto separatorPos = allActions.indexOf(templateSeparator_);
+    if(allActions.size() == separatorPos + 1) {
+        addAction(action);
+    }
+    else {
+        // checking actions from end to start is usually faster
+        for(auto i = allActions.size() - 1; i > separatorPos; --i) {
+            if(QString::compare(action->text(), allActions[i]->text(), Qt::CaseInsensitive) > 0) {
+                if(i == allActions.size() - 1) {
+                    addAction(action);
+                }
+                else {
+                    insertAction(allActions[i + 1], action);
+                }
+                return;
+            }
+        }
+        insertAction(allActions[separatorPos + 1], action);
+    }
+}
+
+void CreateNewMenu::updateTemplateItem(const std::shared_ptr<const TemplateItem> &oldItem, const std::shared_ptr<const TemplateItem> &newItem) {
+    auto allActions = actions();
+    auto separatorPos = allActions.indexOf(templateSeparator_);
+    // all items after the separator are templates
+    for(auto i = separatorPos + 1; i < allActions.size(); ++i) {
+        auto action = static_cast<TemplateAction*>(allActions[i]);
+        if(action->templateItem() == oldItem) {
+            // update the menu item
+            action->setTemplateItem(newItem);
+            break;
+        }
+    }
+}
+
+void CreateNewMenu::removeTemplateItem(const std::shared_ptr<const TemplateItem> &item) {
+    if(!templateSeparator_) {
+        return;
+    }
+    auto allActions = actions();
+    auto separatorPos = allActions.indexOf(templateSeparator_);
+    // all items after the separator are templates
+    for(auto i = separatorPos + 1; i < allActions.size(); ++i) {
+        auto action = static_cast<TemplateAction*>(allActions[i]);
+        if(action->templateItem() == item) {
+            // delete the action from the menu
+            removeAction(action);
+            allActions.removeAt(i);
+            break;
+        }
+    }
+
+    // no more template items. remove the separator
+    if(separatorPos == allActions.size() - 1) {
+        removeAction(templateSeparator_);
+        templateSeparator_ = nullptr;
+    }
+}
+
+} // namespace Fm
diff --git a/src/createnewmenu.h b/src/createnewmenu.h
new file mode 100644 (file)
index 0000000..078103a
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_CREATENEWMENU_H
+#define FM_CREATENEWMENU_H
+
+#include "libfmqtglobals.h"
+#include <QMenu>
+
+#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<const TemplateItem>& item);
+
+    void updateTemplateItem(const std::shared_ptr<const TemplateItem>& oldItem, const std::shared_ptr<const TemplateItem>& newItem);
+
+    void removeTemplateItem(const std::shared_ptr<const TemplateItem>& item);
+
+private:
+    QWidget* dialogParent_;
+    Fm::FilePath dirPath_;
+    QAction* templateSeparator_;
+    std::shared_ptr<Templates> templates_;
+};
+
+}
+
+#endif // FM_CREATENEWMENU_H
diff --git a/src/customaction_p.h b/src/customaction_p.h
new file mode 100644 (file)
index 0000000..7aba3f9
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_CUSTOMACTION_P_H
+#define FM_CUSTOMACTION_P_H
+
+#include <QAction>
+#include "customactions/fileaction.h"
+
+namespace Fm {
+
+class CustomAction : public QAction {
+public:
+    explicit CustomAction(std::shared_ptr<const FileActionItem> 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<const FileActionItem>& item() const {
+        return item_;
+    }
+
+private:
+    std::shared_ptr<const FileActionItem> item_;
+};
+
+} // namespace Fm
+
+#endif
diff --git a/src/customactions/fileaction.cpp b/src/customactions/fileaction.cpp
new file mode 100644 (file)
index 0000000..31d5e3a
--- /dev/null
@@ -0,0 +1,615 @@
+#include "fileaction.h"
+#include <unordered_map>
+#include <QDebug>
+
+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<const char*, shared_ptr<FileActionObject>, 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<FileActionCondition> {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<const FileInfo> 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<FileActionProfile>(kf, *profile_name));
+        }
+    }
+}
+
+std::shared_ptr<FileActionProfile> 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> FileActionItem::fromActionObject(std::shared_ptr<FileActionObject> action_obj, const FileInfoList& files) {
+    std::shared_ptr<FileActionItem> item;
+    if(action_obj->type == FileActionType::MENU) {
+        auto menu = static_pointer_cast<FileActionMenu>(action_obj);
+        if(menu->match(files)) {
+            item = make_shared<FileActionItem>(menu, files);
+            // eliminate empty menus
+            if(item->children.empty()) {
+                item = nullptr;
+            }
+        }
+    }
+    else {
+        // handle profiles here
+        auto action = static_pointer_cast<FileAction>(action_obj);
+        auto profile = action->match(files);
+        if(profile) {
+            item = make_shared<FileActionItem>(action, profile, files);
+        }
+    }
+    return item;
+}
+
+FileActionItem::FileActionItem(std::shared_ptr<FileAction> _action, std::shared_ptr<FileActionProfile> _profile, const FileInfoList& files):
+    FileActionItem{static_pointer_cast<FileActionObject>(_action), files} {
+    profile = _profile;
+}
+
+FileActionItem::FileActionItem(std::shared_ptr<FileActionMenu> menu, const FileInfoList& files):
+    FileActionItem{static_pointer_cast<FileActionObject>(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<FileActionObject> _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<FileActionObject> action;
+                        if(strcmp(type.get(), "Action") == 0) {
+                            action = static_pointer_cast<FileActionObject>(make_shared<FileAction>(kf));
+                            // stdout.printf("load action: %s\n", id);
+                        }
+                        else if(strcmp(type.get(), "Menu") == 0) {
+                            action = static_pointer_cast<FileActionObject>(make_shared<FileActionMenu>(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<const FileActionItem> a, std::shared_ptr<const FileActionItem> 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<FileActionMenu>(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<FileActionMenu>(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
new file mode 100644 (file)
index 0000000..4c2a8bc
--- /dev/null
@@ -0,0 +1,156 @@
+#ifndef FILEACTION_H
+#define FILEACTION_H
+
+#include <glib.h>
+#include <string>
+
+#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<const FileInfo> 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<FileActionCondition> condition;
+
+    // values cached during menu generation
+    bool has_parent;
+};
+
+
+class FileAction: public FileActionObject {
+public:
+
+    FileAction(GKeyFile* kf);
+
+    std::shared_ptr<FileActionProfile> match(const FileInfoList& files) const;
+
+    int target; // bitwise or of FileActionTarget
+    CStrPtr toolbar_label;
+
+    // FIXME: currently we don't support dynamic profiles
+    std::vector<std::shared_ptr<FileActionProfile>> 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<std::shared_ptr<FileActionObject>> cached_children;
+};
+
+
+class FileActionItem {
+public:
+
+    static std::shared_ptr<FileActionItem> fromActionObject(std::shared_ptr<FileActionObject> action_obj, const FileInfoList &files);
+
+    FileActionItem(std::shared_ptr<FileAction> _action, std::shared_ptr<FileActionProfile> _profile, const FileInfoList& files);
+
+    FileActionItem(std::shared_ptr<FileActionMenu> menu, const FileInfoList& files);
+
+    FileActionItem(std::shared_ptr<FileActionObject> _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<FileAction*>(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<std::shared_ptr<const FileActionItem>>& get_sub_items() const {
+        return children;
+    }
+
+    static bool compare_items(std::shared_ptr<const FileActionItem> a, std::shared_ptr<const FileActionItem> b);
+    static std::vector<std::shared_ptr<const FileActionItem>> get_actions_for_files(const FileInfoList& files);
+
+    std::string name;
+    std::string desc;
+    std::string icon;
+    std::shared_ptr<FileActionObject> action;
+    std::shared_ptr<FileActionProfile> profile; // only used by action item
+    std::vector<std::shared_ptr<const FileActionItem>> children; // only used by menu
+};
+
+typedef std::vector<std::shared_ptr<const FileActionItem>> FileActionItemList;
+
+} // namespace Fm
+
+
+#endif // FILEACTION_H
diff --git a/src/customactions/fileactioncondition.cpp b/src/customactions/fileactioncondition.cpp
new file mode 100644 (file)
index 0000000..22d5fa5
--- /dev/null
@@ -0,0 +1,508 @@
+#include "fileactioncondition.h"
+#include "fileaction.h"
+#include <string>
+
+
+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) const {
+    // 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 {
+    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
new file mode 100644 (file)
index 0000000..5a393f1
--- /dev/null
@@ -0,0 +1,123 @@
+#ifndef FILEACTIONCONDITION_H
+#define FILEACTIONCONDITION_H
+
+#include <glib.h>
+#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);
+
+    static 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) const;
+
+    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) const;
+
+    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
new file mode 100644 (file)
index 0000000..0150b1c
--- /dev/null
@@ -0,0 +1,124 @@
+#include "fileactionprofile.h"
+#include "fileaction.h"
+#include <QDebug>
+
+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<FileActionCondition>(kf, group_name.c_str());
+}
+
+
+bool FileActionProfile::launch_once(GAppLaunchContext* /*ctx*/, std::shared_ptr<const FileInfo> 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
new file mode 100644 (file)
index 0000000..18b959f
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef FILEACTIONPROFILE_H
+#define FILEACTIONPROFILE_H
+
+
+#include <string>
+#include <glib.h>
+#include <gio/gio.h>
+
+#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<const FileInfo> 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<FileActionCondition> condition;
+};
+
+} // namespace Fm
+
+#endif // FILEACTIONPROFILE_H
diff --git a/src/dirtreemodel.cpp b/src/dirtreemodel.cpp
new file mode 100644 (file)
index 0000000..c6d030f
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "dirtreemodel.h"
+#include "dirtreemodelitem.h"
+#include <QDebug>
+#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<Fm::FileInfoJob*>(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<size_t>(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<size_t>(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<const Fm::FileInfo> 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<DirTreeModelItem*>(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<const Fm::FileInfo> 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
new file mode 100644 (file)
index 0000000..5c851d0
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_DIRTREEMODEL_H
+#define FM_DIRTREEMODEL_H
+
+#include "libfmqtglobals.h"
+#include <QModelIndex>
+#include <QAbstractItemModel>
+#include <QIcon>
+#include <QList>
+#include <QSharedPointer>
+#include <vector>
+
+#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<const Fm::FileInfo> 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<const Fm::FileInfo> root);
+
+    DirTreeModelItem* itemFromPath(const Fm::FilePath& path) const;
+    DirTreeModelItem* itemFromIndex(const QModelIndex& index) const;
+    QModelIndex indexFromItem(DirTreeModelItem* item) const;
+
+private:
+    bool showHidden_;
+    std::vector<DirTreeModelItem*> rootItems_;
+};
+
+}
+
+#endif // FM_DIRTREEMODEL_H
diff --git a/src/dirtreemodelitem.cpp b/src/dirtreemodelitem.cpp
new file mode 100644 (file)
index 0000000..57ecf41
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "dirtreemodelitem.h"
+#include "dirtreemodel.h"
+#include <QDebug>
+
+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<const Fm::FileInfo> 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<Fm::FileInfoPair>& 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<const Fm::FileInfo> 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<const Fm::FileInfo>& a, const std::shared_ptr<const Fm::FileInfo>& 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("<No sub folders>");
+            QModelIndex placeHolderIndex = placeHolderChild_->index();
+            // qDebug() << "placeHolderIndex: "<<placeHolderIndex;
+            Q_EMIT model->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("<No sub folders>");
+    }
+}
+
+void DirTreeModelItem::onFolderFilesChanged(std::vector<Fm::FileInfoPair> &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("<No sub folders>");
+        }
+    }
+}
+
+
+
+} // namespace Fm
diff --git a/src/dirtreemodelitem.h b/src/dirtreemodelitem.h
new file mode 100644 (file)
index 0000000..d0b562a
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_DIRTREEMODELITEM_H
+#define FM_DIRTREEMODELITEM_H
+
+#include "libfmqtglobals.h"
+#include <vector>
+#include <QIcon>
+#include <QModelIndex>
+
+#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<const Fm::FileInfo> info, DirTreeModel* model, DirTreeModelItem* parent = nullptr);
+    ~DirTreeModelItem();
+
+    void loadFolder();
+    void unloadFolder();
+
+    inline bool isPlaceHolder() const {
+        return (fileInfo_ == nullptr);
+    }
+
+    void setShowHidden(bool show);
+
+    bool isQueuedForDeletion() const {
+        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<const Fm::FileInfo> 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<Fm::FileInfoPair>& changes);
+
+private:
+    std::shared_ptr<const Fm::FileInfo> fileInfo_;
+    std::shared_ptr<Fm::Folder> folder_;
+    QString displayName_ ;
+    QIcon icon_;
+    bool expanded_;
+    bool loaded_;
+    DirTreeModelItem* parent_;
+    DirTreeModelItem* placeHolderChild_;
+    std::vector<DirTreeModelItem*> children_;
+    std::vector<DirTreeModelItem*> 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
new file mode 100644 (file)
index 0000000..7230559
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "dirtreeview.h"
+#include <QHeaderView>
+#include <QDebug>
+#include <QItemSelection>
+#include <QGuiApplication>
+#include <QMouseEvent>
+#include <QTimer>
+#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<DirTreeModel*>(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<DirTreeModel*>(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<DirTreeModel*>(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<DirTreeModel*>(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<std::shared_ptr<const Fm::FileInfo>>();
+        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<Fm::FilePath>(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<QAction*>(sender())) {
+        setCurrentIndex(action->data().toModelIndex());
+    }
+}
+
+void DirTreeView::onNewWindow() {
+    if(QAction* action = qobject_cast<QAction*>(sender())) {
+        auto path = action->data().value<Fm::FilePath>();
+        Q_EMIT openFolderInNewWindowRequested(path);
+    }
+}
+
+void DirTreeView::onNewTab() {
+    if(QAction* action = qobject_cast<QAction*>(sender())) {
+        auto path = action->data().value<Fm::FilePath>();
+        Q_EMIT openFolderInNewTabRequested(path);
+    }
+}
+
+void DirTreeView::onOpenInTerminal() {
+    if(QAction* action = qobject_cast<QAction*>(sender())) {
+        auto path = action->data().value<Fm::FilePath>();
+        Q_EMIT openFolderInTerminalRequested(path);
+    }
+}
+
+void DirTreeView::onNewFolder() {
+    if(QAction* action = qobject_cast<QAction*>(sender())) {
+        auto path = action->data().value<Fm::FilePath>();
+        Q_EMIT createNewFolderRequested(path);
+    }
+}
+
+void DirTreeView::onCollapsed(const QModelIndex& index) {
+    DirTreeModel* treeModel = static_cast<DirTreeModel*>(model());
+    if(treeModel) {
+        treeModel->unloadRow(index);
+    }
+}
+
+void DirTreeView::onExpanded(const QModelIndex& index) {
+    DirTreeModel* treeModel = static_cast<DirTreeModel*>(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<DirTreeModelItem*>(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<DirTreeModel*>(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
new file mode 100644 (file)
index 0000000..1a9cc53
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_DIRTREEVIEW_H
+#define FM_DIRTREEVIEW_H
+
+#include "libfmqtglobals.h"
+#include <QTreeView>
+
+#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<DirTreeModelItem*> queuedForDeletion_;
+};
+
+}
+
+#endif // FM_DIRTREEVIEW_H
diff --git a/src/dndactionmenu.cpp b/src/dndactionmenu.cpp
new file mode 100644 (file)
index 0000000..2688376
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "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
new file mode 100644 (file)
index 0000000..d4dd1bd
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_DNDACTIONMENU_H
+#define FM_DNDACTIONMENU_H
+
+#include "libfmqtglobals.h"
+#include <QMenu>
+#include <QAction>
+
+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
new file mode 100644 (file)
index 0000000..b0f35c9
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "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
new file mode 100644 (file)
index 0000000..d5769e8
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_DNDDEST_H
+#define FM_DNDDEST_H
+
+#include <QMimeData>
+#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() const {
+        return destPath_;
+    }
+
+    static bool isSupported(const QMimeData* data);
+    static 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
new file mode 100644 (file)
index 0000000..8d989e6
--- /dev/null
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>EditBookmarksDialog</class>
+ <widget class="QDialog" name="EditBookmarksDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>480</width>
+    <height>320</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Edit Bookmarks</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="1" column="0">
+    <widget class="QTreeWidget" name="treeWidget">
+     <property name="acceptDrops">
+      <bool>true</bool>
+     </property>
+     <property name="dragEnabled">
+      <bool>true</bool>
+     </property>
+     <property name="dragDropMode">
+      <enum>QAbstractItemView::InternalMove</enum>
+     </property>
+     <property name="defaultDropAction">
+      <enum>Qt::MoveAction</enum>
+     </property>
+     <property name="rootIsDecorated">
+      <bool>false</bool>
+     </property>
+     <property name="itemsExpandable">
+      <bool>false</bool>
+     </property>
+     <attribute name="headerDefaultSectionSize">
+      <number>100</number>
+     </attribute>
+     <column>
+      <property name="text">
+       <string>Name</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Location</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+   <item row="2" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QPushButton" name="addItem">
+       <property name="text">
+        <string>&amp;Add Item</string>
+       </property>
+       <property name="icon">
+        <iconset theme="list-add"/>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="removeItem">
+       <property name="text">
+        <string>&amp;Remove Item</string>
+       </property>
+       <property name="icon">
+        <iconset theme="list-remove"/>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>40</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+    </layout>
+   </item>
+   <item row="0" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>Use drag and drop to reorder the items</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>EditBookmarksDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>EditBookmarksDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/editbookmarksdialog.cpp b/src/editbookmarksdialog.cpp
new file mode 100644 (file)
index 0000000..a36a22f
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "editbookmarksdialog.h"
+#include "ui_edit-bookmarks.h"
+#include <QByteArray>
+#include <QUrl>
+#include <QSaveFile>
+#include <QStandardPaths>
+#include <QDir>
+
+namespace Fm {
+
+EditBookmarksDialog::EditBookmarksDialog(std::shared_ptr<Bookmarks> bookmarks, QWidget* parent, Qt::WindowFlags f):
+    QDialog(parent, f),
+    ui(new Ui::EditBookmarksDialog()),
+    bookmarks_(std::move(bookmarks)) {
+
+    ui->setupUi(this);
+    setAttribute(Qt::WA_DeleteOnClose); // auto delete on close
+
+    // load bookmarks
+    for(const auto& bookmark: bookmarks_->items()) {
+        QTreeWidgetItem* item = new QTreeWidgetItem();
+        item->setData(0, Qt::DisplayRole, bookmark->name());
+        item->setData(1, Qt::DisplayRole, bookmark->path().toString().get());
+        item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled);
+        ui->treeWidget->addTopLevelItem(item);
+    }
+
+    connect(ui->addItem, &QPushButton::clicked, this, &EditBookmarksDialog::onAddItem);
+    connect(ui->removeItem, &QPushButton::clicked, this, &EditBookmarksDialog::onRemoveItem);
+}
+
+EditBookmarksDialog::~EditBookmarksDialog() {
+    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<QTreeWidgetItem*> sels = ui->treeWidget->selectedItems();
+    for(QTreeWidgetItem* const item : sels) {
+        delete item;
+    }
+}
+
+
+} // namespace Fm
diff --git a/src/editbookmarksdialog.h b/src/editbookmarksdialog.h
new file mode 100644 (file)
index 0000000..fb7b3cb
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_EDITBOOKMARKSDIALOG_H
+#define FM_EDITBOOKMARKSDIALOG_H
+
+#include "libfmqtglobals.h"
+#include <QDialog>
+#include "core/bookmarks.h"
+
+namespace Ui {
+class EditBookmarksDialog;
+}
+
+namespace Fm {
+
+class LIBFM_QT_API EditBookmarksDialog : public QDialog {
+    Q_OBJECT
+public:
+    explicit EditBookmarksDialog(std::shared_ptr<Bookmarks> bookmarks, QWidget* parent = 0, Qt::WindowFlags f = 0);
+    virtual ~EditBookmarksDialog();
+
+    virtual void accept();
+
+private Q_SLOTS:
+    void onAddItem();
+    void onRemoveItem();
+
+private:
+    Ui::EditBookmarksDialog* ui;
+    std::shared_ptr<Bookmarks> bookmarks_;
+};
+
+}
+
+#endif // FM_EDITBOOKMARKSDIALOG_H
diff --git a/src/exec-file.ui b/src/exec-file.ui
new file mode 100644 (file)
index 0000000..c5a9ea3
--- /dev/null
@@ -0,0 +1,163 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ExecFileDialog</class>
+ <widget class="QDialog" name="ExecFileDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>487</width>
+    <height>58</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Execute file</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1">
+     <item>
+      <widget class="QLabel" name="icon"/>
+     </item>
+     <item>
+      <widget class="QLabel" name="msg">
+       <property name="text">
+        <string/>
+       </property>
+       <property name="wordWrap">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QPushButton" name="open">
+       <property name="text">
+        <string>&amp;Open</string>
+       </property>
+       <property name="icon">
+        <iconset theme="document-open"/>
+       </property>
+       <property name="default">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="exec">
+       <property name="text">
+        <string>E&amp;xecute</string>
+       </property>
+       <property name="icon">
+        <iconset theme="system-run"/>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="execTerm">
+       <property name="text">
+        <string>Execute in &amp;Terminal</string>
+       </property>
+       <property name="icon">
+        <iconset theme="utilities-terminal"/>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="cancel">
+       <property name="text">
+        <string>Cancel</string>
+       </property>
+       <property name="icon">
+        <iconset theme="dialog-cancel"/>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>cancel</sender>
+   <signal>clicked()</signal>
+   <receiver>ExecFileDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>341</x>
+     <y>39</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>196</x>
+     <y>28</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>exec</sender>
+   <signal>clicked()</signal>
+   <receiver>ExecFileDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>56</x>
+     <y>39</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>196</x>
+     <y>28</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>execTerm</sender>
+   <signal>clicked()</signal>
+   <receiver>ExecFileDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>201</x>
+     <y>39</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>196</x>
+     <y>28</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>open</sender>
+   <signal>clicked()</signal>
+   <receiver>ExecFileDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>346</x>
+     <y>39</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>250</x>
+     <y>28</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/execfiledialog.cpp b/src/execfiledialog.cpp
new file mode 100644 (file)
index 0000000..b0b2780
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "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
new file mode 100644 (file)
index 0000000..8ceae1a
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_EXECFILEDIALOG_H
+#define FM_EXECFILEDIALOG_H
+
+#include "core/basicfilelauncher.h"
+#include "core/fileinfo.h"
+
+#include <QDialog>
+
+#include <memory>
+
+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
new file mode 100644 (file)
index 0000000..47f8c67
--- /dev/null
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FileOperationDialog</class>
+ <widget class="QDialog" name="FileOperationDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>450</width>
+    <height>297</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string/>
+  </property>
+  <layout class="QFormLayout" name="formLayout_2">
+   <item row="0" column="0" colspan="2">
+    <widget class="QLabel" name="message">
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <layout class="QFormLayout" name="formLayout">
+     <property name="fieldGrowthPolicy">
+      <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+     </property>
+     <item row="1" column="0">
+      <widget class="QLabel" name="destLabel">
+       <property name="text">
+        <string>Destination:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="Fm::ElidedLabel" name="dest">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+       <property name="wordWrap">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>Processing:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="Fm::ElidedLabel" name="curFile">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>Preparing...</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="0">
+      <widget class="QLabel" name="label_4">
+       <property name="text">
+        <string>Progress</string>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1">
+      <widget class="QProgressBar" name="progressBar">
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="0">
+      <widget class="QLabel" name="label_5">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>Time remaining:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="5" column="1">
+      <widget class="QLabel" name="timeRemaining">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="0" colspan="2">
+      <widget class="QListWidget" name="sourceFiles">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="0">
+      <widget class="QLabel" name="label_6">
+       <property name="text">
+        <string>Files processed:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="4" column="1">
+      <widget class="QLabel" name="filesProcessed">
+       <property name="text">
+        <string/>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item row="2" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>Fm::ElidedLabel</class>
+   <extends>QLabel</extends>
+   <header>fileoperationdialog_p.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>FileOperationDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>FileOperationDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/file-props.ui b/src/file-props.ui
new file mode 100644 (file)
index 0000000..932e5f0
--- /dev/null
@@ -0,0 +1,800 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FilePropsDialog</class>
+ <widget class="QDialog" name="FilePropsDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>424</width>
+    <height>456</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>File Properties</string>
+  </property>
+  <property name="windowIcon">
+   <iconset theme="document-properties">
+    <normaloff/>
+   </iconset>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="leftMargin">
+    <number>10</number>
+   </property>
+   <property name="topMargin">
+    <number>10</number>
+   </property>
+   <property name="rightMargin">
+    <number>10</number>
+   </property>
+   <property name="bottomMargin">
+    <number>10</number>
+   </property>
+   <item>
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="generalPage">
+      <attribute name="title">
+       <string>General</string>
+      </attribute>
+      <layout class="QFormLayout" name="formLayout_2">
+       <property name="horizontalSpacing">
+        <number>12</number>
+       </property>
+       <property name="verticalSpacing">
+        <number>6</number>
+       </property>
+       <item row="0" column="0">
+        <widget class="QPushButton" name="iconButton">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="icon">
+          <iconset theme="unknown">
+           <normaloff/>
+          </iconset>
+         </property>
+         <property name="iconSize">
+          <size>
+           <width>32</width>
+           <height>32</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QLineEdit" name="fileName"/>
+       </item>
+       <item row="1" column="0">
+        <widget class="QLabel" name="label_2">
+         <property name="text">
+          <string>Location:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QLabel" name="location">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+         <property name="textInteractionFlags">
+          <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="0">
+        <widget class="QLabel" name="label_4">
+         <property name="text">
+          <string>File type:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="1">
+        <widget class="QLabel" name="fileType">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="textInteractionFlags">
+          <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+         </property>
+        </widget>
+       </item>
+       <item row="4" column="0">
+        <widget class="QLabel" name="label">
+         <property name="text">
+          <string>MIME type:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="4" column="1">
+        <widget class="QLabel" name="mimeType">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="textInteractionFlags">
+          <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+         </property>
+        </widget>
+       </item>
+       <item row="6" column="0">
+        <widget class="QLabel" name="label_7">
+         <property name="text">
+          <string>File size:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="6" column="1">
+        <widget class="QLabel" name="fileSize">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="textInteractionFlags">
+          <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+         </property>
+        </widget>
+       </item>
+       <item row="7" column="0">
+        <widget class="QLabel" name="label_6">
+         <property name="text">
+          <string>On-disk size:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="7" column="1">
+        <widget class="QLabel" name="onDiskSize">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="textInteractionFlags">
+          <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+         </property>
+        </widget>
+       </item>
+       <item row="8" column="0">
+        <widget class="QLabel" name="label_3">
+         <property name="text">
+          <string>Last modified:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="8" column="1">
+        <widget class="QLabel" name="lastModified">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="textInteractionFlags">
+          <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="0">
+        <widget class="QLabel" name="targetLabel">
+         <property name="text">
+          <string>Link target:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="1">
+        <widget class="QLabel" name="target">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string/>
+         </property>
+         <property name="wordWrap">
+          <bool>true</bool>
+         </property>
+         <property name="textInteractionFlags">
+          <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+         </property>
+        </widget>
+       </item>
+       <item row="5" column="0">
+        <widget class="QLabel" name="openWithLabel">
+         <property name="text">
+          <string>Open With:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="5" column="1">
+        <widget class="Fm::AppChooserComboBox" name="openWith">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+        </widget>
+       </item>
+       <item row="9" column="0">
+        <widget class="QLabel" name="label_12">
+         <property name="text">
+          <string>Last accessed:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="9" column="1">
+        <widget class="QLabel" name="lastAccessed">
+         <property name="text">
+          <string/>
+         </property>
+         <property name="textInteractionFlags">
+          <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+         </property>
+        </widget>
+       </item>
+       <item row="10" column="0">
+        <widget class="QLabel" name="contentsLabel">
+         <property name="text">
+          <string>Contains:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="10" column="1">
+        <widget class="QLabel" name="fileNumber">
+         <property name="text">
+          <string/>
+         </property>
+        </widget>
+       </item>
+       <item row="11" column="0">
+        <spacer name="verticalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeType">
+          <enum>QSizePolicy::Fixed</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>5</width>
+           <height>5</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="12" column="0">
+        <widget class="QLabel" name="deviceLabel">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="text">
+          <string>Device Usage:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="12" column="1">
+        <layout class="QVBoxLayout" name="verticalLayout_4">
+         <property name="spacing">
+          <number>5</number>
+         </property>
+         <item>
+          <widget class="QProgressBar" name="progressBar">
+           <property name="value">
+            <number>0</number>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLabel" name="spaceLabel">
+           <property name="text">
+            <string/>
+           </property>
+           <property name="wordWrap">
+            <bool>true</bool>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="permissionsPage">
+      <attribute name="title">
+       <string>Permissions</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_2">
+       <property name="spacing">
+        <number>6</number>
+       </property>
+       <item>
+        <widget class="QGroupBox" name="groupBox_2">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="title">
+          <string>Ownership</string>
+         </property>
+         <layout class="QFormLayout" name="formLayout">
+          <property name="horizontalSpacing">
+           <number>12</number>
+          </property>
+          <property name="verticalSpacing">
+           <number>6</number>
+          </property>
+          <item row="1" column="1">
+           <widget class="QLineEdit" name="owner"/>
+          </item>
+          <item row="2" column="1">
+           <widget class="QLineEdit" name="ownerGroup"/>
+          </item>
+          <item row="2" column="0">
+           <widget class="QLabel" name="label_9">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="text">
+             <string>Group:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="0">
+           <widget class="QLabel" name="label_8">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="text">
+             <string>Owner:</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="groupBox">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="title">
+          <string>Access Control</string>
+         </property>
+         <layout class="QVBoxLayout" name="verticalLayout_3">
+          <item>
+           <widget class="QStackedWidget" name="stackedWidget">
+            <property name="sizePolicy">
+             <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+              <horstretch>0</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="currentIndex">
+             <number>0</number>
+            </property>
+            <widget class="QWidget" name="page">
+             <layout class="QFormLayout" name="formLayout_3">
+              <item row="0" column="0">
+               <widget class="QLabel" name="ownerLabel">
+                <property name="text">
+                 <string>Owner:</string>
+                </property>
+               </widget>
+              </item>
+              <item row="0" column="1">
+               <widget class="QComboBox" name="ownerPerm">
+                <property name="sizePolicy">
+                 <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+                  <horstretch>0</horstretch>
+                  <verstretch>0</verstretch>
+                 </sizepolicy>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="0">
+               <widget class="QLabel" name="groupLabel">
+                <property name="text">
+                 <string>Group:</string>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="1">
+               <widget class="QComboBox" name="groupPerm">
+                <property name="sizePolicy">
+                 <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+                  <horstretch>0</horstretch>
+                  <verstretch>0</verstretch>
+                 </sizepolicy>
+                </property>
+               </widget>
+              </item>
+              <item row="2" column="0">
+               <widget class="QLabel" name="otherLabel">
+                <property name="text">
+                 <string>Other:</string>
+                </property>
+               </widget>
+              </item>
+              <item row="2" column="1">
+               <widget class="QComboBox" name="otherPerm">
+                <property name="sizePolicy">
+                 <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+                  <horstretch>0</horstretch>
+                  <verstretch>0</verstretch>
+                 </sizepolicy>
+                </property>
+               </widget>
+              </item>
+              <item row="3" column="0" colspan="2">
+               <widget class="QCheckBox" name="executable">
+                <property name="text">
+                 <string>Make the file executable</string>
+                </property>
+                <property name="tristate">
+                 <bool>true</bool>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </widget>
+            <widget class="QWidget" name="page_2">
+             <layout class="QGridLayout" name="gridLayout_2" columnstretch="1,0,0">
+              <item row="0" column="0" rowspan="3">
+               <layout class="QGridLayout" name="gridLayout">
+                <property name="rightMargin">
+                 <number>0</number>
+                </property>
+                <property name="spacing">
+                 <number>6</number>
+                </property>
+                <item row="0" column="0">
+                 <widget class="QLabel" name="label_5">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text">
+                   <string>Owner:</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="0" column="1">
+                 <widget class="QCheckBox" name="checkBox">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text">
+                   <string>Read</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="0" column="2">
+                 <widget class="QCheckBox" name="checkBox_5">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text">
+                   <string>Write</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="0" column="3">
+                 <widget class="QCheckBox" name="checkBox_8">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text">
+                   <string>Execute</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="1" column="0">
+                 <widget class="QLabel" name="label_10">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text">
+                   <string>Group:</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="1" column="1">
+                 <widget class="QCheckBox" name="checkBox_4">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text">
+                   <string>Read</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="1" column="2">
+                 <widget class="QCheckBox" name="checkBox_9">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text">
+                   <string>Write</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="1" column="3">
+                 <widget class="QCheckBox" name="checkBox_3">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text">
+                   <string>Execute</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="2" column="0">
+                 <widget class="QLabel" name="label_11">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Fixed" vsizetype="Preferred">
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text">
+                   <string>Other:</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="2" column="1">
+                 <widget class="QCheckBox" name="checkBox_7">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text">
+                   <string>Read</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="2" column="2">
+                 <widget class="QCheckBox" name="checkBox_2">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text">
+                   <string>Write</string>
+                  </property>
+                 </widget>
+                </item>
+                <item row="2" column="3">
+                 <widget class="QCheckBox" name="checkBox_6">
+                  <property name="sizePolicy">
+                   <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                   </sizepolicy>
+                  </property>
+                  <property name="text">
+                   <string>Execute</string>
+                  </property>
+                 </widget>
+                </item>
+               </layout>
+              </item>
+              <item row="0" column="2">
+               <widget class="QCheckBox" name="checkBox_10">
+                <property name="text">
+                 <string>Sticky</string>
+                </property>
+               </widget>
+              </item>
+              <item row="1" column="2">
+               <widget class="QCheckBox" name="checkBox_11">
+                <property name="text">
+                 <string>SetUID</string>
+                </property>
+               </widget>
+              </item>
+              <item row="2" column="2">
+               <widget class="QCheckBox" name="checkBox_12">
+                <property name="text">
+                 <string>SetGID</string>
+                </property>
+               </widget>
+              </item>
+              <item row="0" column="1" rowspan="3">
+               <widget class="Line" name="line">
+                <property name="orientation">
+                 <enum>Qt::Vertical</enum>
+                </property>
+               </widget>
+              </item>
+             </layout>
+            </widget>
+           </widget>
+          </item>
+          <item>
+           <layout class="QHBoxLayout" name="horizontalLayout">
+            <item>
+             <spacer name="horizontalSpacer">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item>
+             <widget class="QPushButton" name="pushButton">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="text">
+               <string>Advanced Mode</string>
+              </property>
+              <property name="checkable">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>Fm::AppChooserComboBox</class>
+   <extends>QComboBox</extends>
+   <header>appchoosercombobox.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>FilePropsDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>FilePropsDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/filedialog.cpp b/src/filedialog.cpp
new file mode 100644 (file)
index 0000000..4228216
--- /dev/null
@@ -0,0 +1,989 @@
+#include "filedialog.h"
+#include "cachedfoldermodel.h"
+#include "proxyfoldermodel.h"
+#include "utilities.h"
+#include "core/fileinfojob.h"
+#include "ui_filedialog.h"
+
+#include <QDialogButtonBox>
+#include <QPushButton>
+#include <QMimeType>
+#include <QMimeDatabase>
+#include <QMessageBox>
+#include <QToolBar>
+#include <QCompleter>
+#include <QShortcut>
+#include <QTimer>
+#include <QDebug>
+
+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<void(QCompleter::*)(const QString &)>(&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) {
+            if(!defaultSuffix_.isEmpty() && name.lastIndexOf('.') == -1) {
+                name += '.';
+                name += defaultSuffix_;
+            }
+            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, [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);
+
+    // use generic icons for view actions only if theme icons don't exist
+    iconViewAction_ = toolbar->addAction(QIcon::fromTheme(QLatin1String("view-list-icons"), style()->standardIcon(QStyle::SP_FileDialogContentsView)), tr("Icon View"));
+    iconViewAction_->setCheckable(true);
+    connect(iconViewAction_, &QAction::toggled, this, &FileDialog::onViewModeToggled);
+    viewModeGroup->addAction(iconViewAction_);
+    thumbnailViewAction_ = toolbar->addAction(QIcon::fromTheme(QLatin1String("dialog-information"), style()->standardIcon(QStyle::SP_FileDialogInfoView)), tr("Thumbnail View"));
+    thumbnailViewAction_->setCheckable(true);
+    connect(thumbnailViewAction_, &QAction::toggled, this, &FileDialog::onViewModeToggled);
+    viewModeGroup->addAction(thumbnailViewAction_);
+    compactViewAction_ = toolbar->addAction(QIcon::fromTheme(QLatin1String("view-list-text"), style()->standardIcon(QStyle::SP_FileDialogListView)), tr("Compact View"));
+    compactViewAction_->setCheckable(true);
+    connect(compactViewAction_, &QAction::toggled, this, &FileDialog::onViewModeToggled);
+    viewModeGroup->addAction(compactViewAction_);
+    detailedViewAction_ = toolbar->addAction(QIcon::fromTheme(QLatin1String("view-list-details"), 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<int> 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
+#if (QT_VERSION >= QT_VERSION_CHECK(5,12,0))
+            QRegularExpression sep{"\"\\s+\""};  // separated with " "
+#else
+            QRegExp sep{"\"\\s+\""};  // separated with " "
+#endif
+            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<const Fm::FileInfo> FileDialog::firstSelectedDir() const {
+    std::shared_ptr<const Fm::FileInfo> 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<const Fm::FileInfo> 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);
+            if(!defaultSuffix_.isEmpty() && firstName.lastIndexOf('.') == -1) {
+                firstName += '.';
+                firstName += defaultSuffix_;
+            }
+            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, [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 &current, 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<const FileInfo> &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<QAction*>(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<FileInfoJob*>(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<QUrl> 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<const FileInfo> &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 (QT_VERSION >= QT_VERSION_CHECK(5,12,0))
+        if(name.indexOf(pattern) == 0) {
+#else
+        if(pattern.exactMatch(name)) {
+#endif
+            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 QRegularExpression objects
+    auto globs = nameFilter.simplified().split(' ');
+    for(const auto& glob: globs) {
+#if (QT_VERSION >= QT_VERSION_CHECK(5,12,0))
+        patterns_.emplace_back(QRegularExpression("\\A(?:"
+                                                    + QRegularExpression::wildcardToRegularExpression(glob)
+                                                    + ")\\z", QRegularExpression::CaseInsensitiveOption));
+#else
+        patterns_.emplace_back(QRegExp(glob, Qt::CaseInsensitive, QRegExp::Wildcard));
+#endif
+    }
+}
+
+} // namespace Fm
diff --git a/src/filedialog.h b/src/filedialog.h
new file mode 100644 (file)
index 0000000..772b3dd
--- /dev/null
@@ -0,0 +1,221 @@
+#ifndef FM_FILEDIALOG_H
+#define FM_FILEDIALOG_H
+
+#include "libfmqtglobals.h"
+#include "core/filepath.h"
+
+#include <QFileDialog>
+#if (QT_VERSION >= QT_VERSION_CHECK(5,12,0))
+#include <QRegularExpression>
+#else
+#include <QRegExp>
+#endif
+#include <vector>
+#include <memory>
+#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<QUrl> 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 &current, const QModelIndex& /*previous*/);
+    void onSelectionChanged(const QItemSelection& /*selected*/, const QItemSelection& /*deselected*/);
+    void onFileClicked(int type, const std::shared_ptr<const Fm::FileInfo>& 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<QUrl> &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<const Fm::FileInfo>& info) const override;
+        void update();
+
+        FileDialog* dlg_;
+#if (QT_VERSION >= QT_VERSION_CHECK(5,12,0))
+        std::vector<QRegularExpression> patterns_;
+#else
+        std::vector<QRegExp> patterns_;
+#endif
+    };
+
+    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<const Fm::FileInfo> 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::FileDialog> ui;
+    CachedFolderModel* folderModel_;
+    ProxyFolderModel* proxyModel_;
+    FilePath directoryPath_;
+    std::shared_ptr<Fm::Folder> 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<QUrl> 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
new file mode 100644 (file)
index 0000000..cc7d65e
--- /dev/null
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>FileDialog</class>
+ <widget class="QDialog" name="FileDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>700</width>
+    <height>500</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string/>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout" stretch="0,1,0">
+   <item>
+    <layout class="QHBoxLayout" name="toolbarLayout" stretch="0,1">
+     <item>
+      <widget class="QLabel" name="lookInLabel">
+       <property name="text">
+        <string>Location:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="Fm::PathBar" name="location" native="true"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QSplitter" name="splitter">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <widget class="Fm::SidePane" name="sidePane" native="true">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="minimumSize">
+       <size>
+        <width>120</width>
+        <height>0</height>
+       </size>
+      </property>
+     </widget>
+     <widget class="Fm::FolderView" name="folderView" native="true">
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
+        <horstretch>1</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="0">
+      <widget class="QLabel" name="fileNameLabel">
+       <property name="text">
+        <string>File name:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QLineEdit" name="fileName"/>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="fileTypeLabel">
+       <property name="text">
+        <string>File type:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="QComboBox" name="fileTypeCombo"/>
+     </item>
+     <item row="0" column="2" rowspan="2">
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>Fm::SidePane</class>
+   <extends>QWidget</extends>
+   <header location="global">sidepane.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>Fm::FolderView</class>
+   <extends>QWidget</extends>
+   <header>folderview.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>Fm::PathBar</class>
+   <extends>QWidget</extends>
+   <header>pathbar.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>FileDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>FileDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/filedialoghelper.cpp b/src/filedialoghelper.cpp
new file mode 100644 (file)
index 0000000..310015f
--- /dev/null
@@ -0,0 +1,290 @@
+#include "filedialoghelper.h"
+
+#include "libfmqt.h"
+#include "filedialog.h"
+
+#include <QWindow>
+#include <QDebug>
+#include <QTimer>
+#include <QSettings>
+#include <QtGlobal>
+
+#include <memory>
+
+namespace Fm {
+
+inline static const QString viewModeToString(Fm::FolderView::ViewMode value);
+inline static Fm::FolderView::ViewMode viewModeFromString(const QString& str);
+
+FileDialogHelper::FileDialogHelper() {
+    // can only be used after libfm-qt initialization
+    dlg_ = std::unique_ptr<Fm::FileDialog>(new Fm::FileDialog());
+    connect(dlg_.get(), &Fm::FileDialog::accepted, [this]() {
+        saveSettings();
+        accept();
+    });
+    connect(dlg_.get(), &Fm::FileDialog::rejected, [this]() {
+        saveSettings();
+        reject();
+    });
+
+    connect(dlg_.get(), &Fm::FileDialog::fileSelected, this, &FileDialogHelper::fileSelected);
+    connect(dlg_.get(), &Fm::FileDialog::filesSelected, this, &FileDialogHelper::filesSelected);
+    connect(dlg_.get(), &Fm::FileDialog::currentChanged, this, &FileDialogHelper::currentChanged);
+    connect(dlg_.get(), &Fm::FileDialog::directoryEntered, this, &FileDialogHelper::directoryEntered);
+    connect(dlg_.get(), &Fm::FileDialog::filterSelected, this, &FileDialogHelper::filterSelected);
+}
+
+FileDialogHelper::~FileDialogHelper() {
+}
+
+void FileDialogHelper::exec() {
+    dlg_->exec();
+}
+
+bool FileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow* parent) {
+    dlg_->setAttribute(Qt::WA_NativeWindow, true); // without this, sometimes windowHandle() will return nullptr
+
+    dlg_->setWindowFlags(windowFlags);
+    dlg_->setWindowModality(windowModality);
+
+    // Reference: KDE implementation
+    // https://github.com/KDE/plasma-integration/blob/master/src/platformtheme/kdeplatformfiledialoghelper.cpp
+    dlg_->windowHandle()->setTransientParent(parent);
+
+    applyOptions();
+
+    loadSettings();
+    // central positioning with respect to the parent window
+    if(parent && parent->isVisible()) {
+        dlg_->move(parent->x() + (parent->width() - dlg_->width()) / 2,
+                   parent->y() + (parent->height() - dlg_->height()) / 2);
+    }
+
+    // NOTE: the timer here is required as a workaround borrowed from KDE. Without this, the dialog UI will be blocked.
+    // QFileDialog calls our platform plugin to show our own native file dialog instead of showing its widget.
+    // However, it still creates a hidden dialog internally, and then make it modal.
+    // So user input from all other windows that are not the children of the QFileDialog widget will be blocked.
+    // This includes our own dialog. After the return of this show() method, QFileDialog creates its own window and
+    // then make it modal, which blocks our UI. The timer schedule a delayed popup of our file dialog, so we can
+    // show again after QFileDialog and override the modal state. Then our UI can be unblocked.
+    QTimer::singleShot(0, dlg_.get(), &QDialog::show);
+    dlg_->setFocus();
+    return true;
+}
+
+void FileDialogHelper::hide() {
+    dlg_->hide();
+}
+
+bool FileDialogHelper::defaultNameFilterDisables() const {
+    return false;
+}
+
+void FileDialogHelper::setDirectory(const QUrl& directory) {
+    dlg_->setDirectory(directory);
+}
+
+QUrl FileDialogHelper::directory() const {
+    return dlg_->directory();
+}
+
+void FileDialogHelper::selectFile(const QUrl& filename) {
+    dlg_->selectFile(filename);
+}
+
+QList<QUrl> FileDialogHelper::selectedFiles() const {
+    return dlg_->selectedFiles();
+}
+
+void FileDialogHelper::setFilter() {
+    // FIXME: what's this?
+    // The gtk+ 3 file dialog helper in Qt5 update options in this method.
+    applyOptions();
+}
+
+void FileDialogHelper::selectNameFilter(const QString& filter) {
+    dlg_->selectNameFilter(filter);
+}
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
+QString FileDialogHelper::selectedMimeTypeFilter() const {
+    return dlg_->selectedMimeTypeFilter();
+}
+
+void FileDialogHelper::selectMimeTypeFilter(const QString& filter) {
+    dlg_->selectMimeTypeFilter(filter);
+}
+#endif
+
+QString FileDialogHelper::selectedNameFilter() const {
+    return dlg_->selectedNameFilter();
+}
+
+bool FileDialogHelper::isSupportedUrl(const QUrl& url) const {
+    return dlg_->isSupportedUrl(url);
+}
+
+void FileDialogHelper::applyOptions() {
+    auto& opt = options();
+
+    // set title
+    if(opt->windowTitle().isEmpty()) {
+        dlg_->setWindowTitle(opt->acceptMode() == QFileDialogOptions::AcceptOpen ? tr("Open File")
+                                                                                 : tr("Save File"));
+    }
+    else {
+        dlg_->setWindowTitle(opt->windowTitle());
+    }
+
+    dlg_->setFilter(opt->filter());
+    dlg_->setFileMode(QFileDialog::FileMode(opt->fileMode()));
+    dlg_->setAcceptMode(QFileDialog::AcceptMode(opt->acceptMode())); // also sets a default label for accept button
+    // bool useDefaultNameFilters() const;
+    dlg_->setNameFilters(opt->nameFilters());
+    if(!opt->mimeTypeFilters().empty()) {
+        dlg_->setMimeTypeFilters(opt->mimeTypeFilters());
+    }
+
+    dlg_->setDefaultSuffix(opt->defaultSuffix());
+    // QStringList history() const;
+
+    // explicitly set labels
+    for(int i = 0; i < QFileDialogOptions::DialogLabelCount; ++i) {
+        auto label = static_cast<QFileDialogOptions::DialogLabel>(i);
+        if(opt->isLabelExplicitlySet(label)) {
+            dlg_->setLabelText(static_cast<QFileDialog::DialogLabel>(label), opt->labelText(label));
+        }
+    }
+
+    auto url = opt->initialDirectory();
+    if(url.isValid()) {
+        dlg_->setDirectory(url);
+    }
+
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
+    auto filter = opt->initiallySelectedMimeTypeFilter();
+    if(!filter.isEmpty()) {
+        selectMimeTypeFilter(filter);
+    }
+    else {
+        filter = opt->initiallySelectedNameFilter();
+        if(!filter.isEmpty()) {
+            selectNameFilter(opt->initiallySelectedNameFilter());
+        }
+    }
+#else
+    auto filter = opt->initiallySelectedNameFilter();
+    if(!filter.isEmpty()) {
+        selectNameFilter(filter);
+    }
+#endif
+
+    auto selectedFiles = opt->initiallySelectedFiles();
+    for(const auto& selectedFile: selectedFiles) {
+        selectFile(selectedFile);
+    }
+    // QStringList supportedSchemes() const;
+}
+
+static const QString viewModeToString(Fm::FolderView::ViewMode value) {
+    QString ret;
+    switch(value) {
+    case Fm::FolderView::DetailedListMode:
+    default:
+        ret = QLatin1String("Detailed");
+        break;
+    case Fm::FolderView::CompactMode:
+        ret = QLatin1String("Compact");
+        break;
+    case Fm::FolderView::IconMode:
+        ret = QLatin1String("Icon");
+        break;
+    case Fm::FolderView::ThumbnailMode:
+        ret = QLatin1String("Thumbnail");
+        break;
+    }
+    return ret;
+}
+
+Fm::FolderView::ViewMode viewModeFromString(const QString& str) {
+    Fm::FolderView::ViewMode ret;
+    if(str == QLatin1String("Detailed")) {
+        ret = Fm::FolderView::DetailedListMode;
+    }
+    else if(str == QLatin1String("Compact")) {
+        ret = Fm::FolderView::CompactMode;
+    }
+    else if(str == QLatin1String("Icon")) {
+        ret = Fm::FolderView::IconMode;
+    }
+    else if(str == QLatin1String("Thumbnail")) {
+        ret = Fm::FolderView::ThumbnailMode;
+    }
+    else {
+        ret = Fm::FolderView::DetailedListMode;
+    }
+    return ret;
+}
+
+void FileDialogHelper::loadSettings() {
+    QSettings settings(QSettings::UserScope, "lxqt", "filedialog");
+    settings.beginGroup ("Sizes");
+    dlg_->resize(settings.value("WindowSize", QSize(700, 500)).toSize());
+    dlg_->setSplitterPos(settings.value("SplitterPos", 200).toInt());
+    settings.endGroup();
+
+   settings.beginGroup ("View");
+   dlg_->setViewMode(viewModeFromString(settings.value("Mode", "Detailed").toString()));
+   settings.endGroup();
+}
+
+void FileDialogHelper::saveSettings() {
+    QSettings settings(QSettings::UserScope, "lxqt", "filedialog");
+    settings.beginGroup ("Sizes");
+    QSize windowSize = dlg_->size();
+    if(settings.value("WindowSize") != windowSize) { // no redundant write
+        settings.setValue("WindowSize", windowSize);
+    }
+    int splitterPos = dlg_->splitterPos();
+    if(settings.value("SplitterPos") != splitterPos) {
+        settings.setValue("SplitterPos", splitterPos);
+    }
+    settings.endGroup();
+
+    settings.beginGroup ("View");
+    QString mode = viewModeToString(dlg_->viewMode());
+    if(settings.value("Mode") != mode) {
+        settings.setValue("Mode", mode);
+    }
+    settings.endGroup();
+}
+
+/*
+FileDialogPlugin::FileDialogPlugin() {
+
+}
+
+QPlatformFileDialogHelper *FileDialogPlugin::createHelper() {
+    return new FileDialogHelper();
+}
+*/
+
+} // namespace Fm
+
+
+QPlatformFileDialogHelper *createFileDialogHelper() {
+    // When a process has this environment set, that means glib event loop integration is disabled.
+    // In this case, libfm just won't work. So let's disable the file dialog helper and return nullptr.
+    if(qgetenv("QT_NO_GLIB") == "1") {
+        return nullptr;
+    }
+
+    static std::unique_ptr<Fm::LibFmQt> libfmQtContext_;
+    if(!libfmQtContext_) {
+        // initialize libfm-qt only once
+        libfmQtContext_ = std::unique_ptr<Fm::LibFmQt>{new Fm::LibFmQt()};
+    }
+    return new Fm::FileDialogHelper{};
+}
diff --git a/src/filedialoghelper.h b/src/filedialoghelper.h
new file mode 100644 (file)
index 0000000..af5c87b
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef FILEDIALOGHELPER_H
+#define FILEDIALOGHELPER_H
+
+#include "libfmqtglobals.h"
+#include <qpa/qplatformdialoghelper.h> // this private header is subject to changes
+#include <memory>
+
+namespace Fm {
+
+class FileDialog;
+
+class LIBFM_QT_API FileDialogHelper : public QPlatformFileDialogHelper {
+    Q_OBJECT
+
+public:
+    FileDialogHelper();
+
+    virtual ~FileDialogHelper();
+
+    // QPlatformDialogHelper
+    void exec() override;
+    bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override;
+    void hide() override;
+
+    // QPlatformFileDialogHelper
+    bool defaultNameFilterDisables() const override;
+    void setDirectory(const QUrl &directory) override;
+    QUrl directory() const override;
+    void selectFile(const QUrl &filename) override;
+    QList<QUrl> selectedFiles() const override;
+    void setFilter() override;
+    void selectNameFilter(const QString &filter) override;
+#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
+    QString selectedMimeTypeFilter() const override;
+    void selectMimeTypeFilter(const QString &filter) override;
+#endif
+    QString selectedNameFilter() const override;
+
+    bool isSupportedUrl(const QUrl &url) const override;
+
+private:
+    void applyOptions();
+    void loadSettings();
+    void saveSettings();
+
+private:
+    std::unique_ptr<Fm::FileDialog> dlg_;
+};
+
+} // namespace Fm
+
+// export a C API without C++ name mangling so others can dynamically load libfm-qt at runtime
+// to call this API and get a new QPlatformFileDialogHelper object.
+
+extern "C" {
+
+// if the process calling this API fail to load libfm-qt, nullptr will be returned instead.
+LIBFM_QT_API QPlatformFileDialogHelper* createFileDialogHelper();
+
+}
+
+#endif // FILEDIALOGHELPER_H
diff --git a/src/filelauncher.cpp b/src/filelauncher.cpp
new file mode 100644 (file)
index 0000000..ff14533
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "filelauncher.h"
+#include "applaunchcontext.h"
+#include <QMessageBox>
+#include <QDebug>
+#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<FmAppLaunchContext> context{fm_app_launch_context_new_for_widget(parent), false};
+    bool ret = BasicFileLauncher::launchFiles(file_infos, G_APP_LAUNCH_CONTEXT(context.get()));
+    return ret;
+}
+
+bool FileLauncher::launchPaths(QWidget* parent, const FilePathList& paths) {
+    GObjectPtr<FmAppLaunchContext> context{fm_app_launch_context_new_for_widget(parent), false};
+    bool ret = BasicFileLauncher::launchPaths(paths, G_APP_LAUNCH_CONTEXT(context.get()));
+    return ret;
+}
+
+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*/, const 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
new file mode 100644 (file)
index 0000000..991a00a
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_FILELAUNCHER_H
+#define FM_FILELAUNCHER_H
+
+#include "libfmqtglobals.h"
+#include <QWidget>
+#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, const 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
new file mode 100644 (file)
index 0000000..918c31f
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "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 <QMessageBox>
+#include <QAbstractItemView>
+#include <QDebug>
+#include "filemenu_p.h"
+
+#include "core/archiver.h"
+
+#include "core/legacy/fm-app-info.h"
+
+
+namespace Fm {
+
+FileMenu::FileMenu(Fm::FileInfoList files, std::shared_ptr<const Fm::FileInfo> 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_) {
+        auto archiver = Archiver::defaultArchiver();
+        if(archiver) {
+            if(sameType_ && 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::addTrustAction() {
+    if(info_->isExecutableType() && (!fileLauncher_ || !fileLauncher_->quickExec())) {
+        QAction* trustAction = new QAction(files_.size() > 1
+                                             ? tr("Trust selected executables")
+                                             : tr("Trust this executable"),
+                                           this);
+        trustAction->setCheckable(true);
+        trustAction->setChecked(info_->isTrustable());
+        connect(trustAction, &QAction::toggled, this, &FileMenu::onTrustToggled);
+        insertAction(propertiesAction_, trustAction);
+    }
+}
+
+void FileMenu::addCustomActionItem(QMenu* menu, std::shared_ptr<const FileActionItem> 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<AppInfoAction*>(sender());
+    openFilesWithApp(action->appInfo().get());
+}
+
+void FileMenu::onCustomActionTrigerred() {
+    CustomAction* action = static_cast<CustomAction*>(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::onTrustToggled(bool checked) {
+    for(auto& file: files_) {
+        file->setTrustable(checked);
+    }
+}
+
+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<QAbstractItemView*>(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
new file mode 100644 (file)
index 0000000..24c49ed
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_FILEMENU_H
+#define FM_FILEMENU_H
+
+#include "libfmqtglobals.h"
+#include <QMenu>
+#include <qabstractitemmodel.h>
+#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<const Fm::FileInfo> info, Fm::FilePath cwd, bool isWritableDir = true, const QString& title = QString(), QWidget* parent = nullptr);
+    ~FileMenu();
+
+    void addTrustAction();
+
+    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<const Fm::FileInfo>& 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<const FileActionItem> item);
+    void openFilesWithApp(GAppInfo* app);
+
+protected Q_SLOTS:
+    void onOpenTriggered();
+    void onOpenWithTriggered();
+    void onTrustToggled(bool checked);
+    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<const Fm::FileInfo> 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
new file mode 100644 (file)
index 0000000..9cbd6ee
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_FILEMENU_P_H
+#define FM_FILEMENU_P_H
+
+#include <QDebug>
+#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
new file mode 100644 (file)
index 0000000..1e5d582
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "fileoperation.h"
+#include "fileoperationdialog.h"
+#include <QTimer>
+#include <QElapsedTimer>
+#include <QMessageBox>
+#include <QDebug>
+
+#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<FileTransferJob*>(job_)->setDestDirPath(destPath_);
+        }
+        break;
+    default:
+        break;
+    }
+}
+
+void FileOperation::setDestFiles(FilePathList destFiles) {
+    switch(type_) {
+    case Copy:
+    case Move:
+    case Link:
+        if(job_) {
+            static_cast<FileTransferJob*>(job_)->setDestPaths(std::move(destFiles));
+        }
+        break;
+    default:
+        break;
+    }
+}
+
+void FileOperation::setChmod(mode_t newMode, mode_t newModeMask) {
+    if(job_) {
+        auto job = static_cast<FileChangeAttrJob*>(job_);
+        job->setFileModeEnabled(true);
+        job->setFileMode(newMode, newModeMask);
+    }
+}
+
+void FileOperation::setChown(uid_t uid, gid_t gid) {
+    if(job_) {
+        auto job = static_cast<FileChangeAttrJob*>(job_);
+        if(uid != INVALID_UID) {
+            job->setOwnerEnabled(true);
+            job->setOwner(uid);
+        }
+        if(gid != INVALID_GID) {
+            job->setGroupEnabled(true);
+            job->setGroup(gid);
+        }
+    }
+}
+
+void FileOperation::setRecursiveChattr(bool recursive) {
+    if(job_) {
+        auto job = static_cast<FileChangeAttrJob*>(job_);
+        job->setRecursive(recursive);
+    }
+}
+
+bool FileOperation::run() {
+    delete uiTimer_;
+    // 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<Fm::TrashJob*>(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
new file mode 100644 (file)
index 0000000..46c5215
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_FILEOPERATION_H
+#define FM_FILEOPERATION_H
+
+#include "libfmqtglobals.h"
+#include <QObject>
+#include <QElapsedTimer>
+#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
new file mode 100644 (file)
index 0000000..18eff7e
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "fileoperationdialog.h"
+#include "fileoperation.h"
+#include "renamedialog.h"
+#include <QLabel>
+#include <QMessageBox>
+#include "utilities.h"
+#include "ui_file-operation-dialog.h"
+
+#include "core/legacy/fm-config.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
new file mode 100644 (file)
index 0000000..71f5af0
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_FILEOPERATIONDIALOG_H
+#define FM_FILEOPERATIONDIALOG_H
+
+#include "libfmqtglobals.h"
+#include <cstdint>
+#include <QDialog>
+#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
new file mode 100644 (file)
index 0000000..a73f3f4
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_FILEOPERATIONDIALOG_P_H
+#define FM_FILEOPERATIONDIALOG_P_H
+
+#include <QPainter>
+#include <QStyleOption>
+#include <QLabel>
+
+namespace Fm {
+
+class ElidedLabel : public QLabel {
+Q_OBJECT
+
+public:
+    explicit ElidedLabel(QWidget *parent = 0, Qt::WindowFlags f = Qt::WindowFlags()):
+        QLabel(parent, f),
+        lastWidth_(0) {
+            setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+            // set a min width to prevent the window from widening with long texts
+            setMinimumWidth(fontMetrics().averageCharWidth() * 10);
+        }
+
+protected:
+    // A simplified version of QLabel::paintEvent() without pixmap or shortcut but with eliding.
+    void paintEvent(QPaintEvent* /*event*/) override {
+        QRect cr = contentsRect().adjusted(margin(), margin(), -margin(), -margin());
+        QString txt = text();
+        // if the text is changed or its rect is resized (due to window resizing),
+        // find whether it needs to be elided...
+        if (txt != lastText_ || cr.width() != lastWidth_) {
+            lastText_ = txt;
+            lastWidth_ = cr.width();
+            elidedText_ = fontMetrics().elidedText(txt, Qt::ElideMiddle, cr.width());
+        }
+        // ... then, draw the (elided) text
+        if(!elidedText_.isEmpty()) {
+            QPainter painter(this);
+            QStyleOption opt;
+            opt.initFrom(this);
+            style()->drawItemText(&painter, cr, alignment(), opt.palette, isEnabled(), elidedText_, foregroundRole());
+        }
+    }
+
+private:
+    QString elidedText_;
+    QString lastText_;
+    int lastWidth_;
+};
+
+}
+
+#endif // FM_FILEOPERATIONDIALOG_P_H
diff --git a/src/filepropsdialog.cpp b/src/filepropsdialog.cpp
new file mode 100644 (file)
index 0000000..f6b68ac
--- /dev/null
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "filepropsdialog.h"
+#include "ui_file-props.h"
+#include "utilities.h"
+#include "fileoperation.h"
+#include <QStringBuilder>
+#include <QStringListModel>
+#include <QMessageBox>
+#include <QDateTime>
+#include <QStandardPaths>
+#include <QFileDialog>
+#include <sys/types.h>
+#include <time.h>
+#include "core/totalsizejob.h"
+#include "core/folder.h"
+
+#include "core/legacy/fm-config.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();
+
+    if(!singleFile || !hasDir) { // not a single dir
+        ui->contentsLabel->hide();
+        ui->fileNumber->hide();
+    }
+}
+
+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_t>(uid) != fi->uid()) {
+            uid = DIFFERENT_UIDS;    // not all files have the same owner
+        }
+        if(gid != DIFFERENT_GIDS && static_cast<gid_t>(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<const Fm::IconInfo> 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();
+
+    // disk usage
+    auto folder = Fm::Folder::fromPath(fileInfo->dirPath());
+    if(!folder->isLoaded() && fileInfo->isDir()) { // an empty space is right clicked
+        folder = Fm::Folder::fromPath(fileInfo->path());
+    }
+    guint64 free, total;
+    if(folder->getFilesystemInfo(&total, &free)) {
+        ui->progressBar->setValue(qRound(static_cast<qreal>((total - free) * 100) / static_cast<qreal>(total)));
+        ui->progressBar->setFormat(tr("%p% used"));
+        ui->spaceLabel->setText(tr("%1 Free of %2")
+                                .arg(formatFileSize(free, false))
+                                .arg(formatFileSize(total, false)));
+    }
+    else {
+        ui->deviceLabel->setVisible(false);
+        ui->spaceLabel->setVisible(false);
+        ui->progressBar->setVisible(false);
+    }
+}
+
+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);
+
+        if(ui->contentsLabel->isVisible()) {
+            unsigned int fc =  totalSizeJob->fileCount(); // the directory is included
+            if (fc <= 1)
+                str = tr("no file");
+            else if (fc == 2)
+                str = tr("one file");
+            else
+                str = tr("%Ln files", "", fc - 1);
+            ui->fileNumber->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<const Fm::IconInfo> 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
new file mode 100644 (file)
index 0000000..67a68d4
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_FILEPROPSDIALOG_H
+#define FM_FILEPROPSDIALOG_H
+
+#include "libfmqtglobals.h"
+#include <QDialog>
+#include <QTimer>
+
+#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<const Fm::FileInfo> 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<const Fm::FileInfo> 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<const Fm::MimeType> 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
new file mode 100644 (file)
index 0000000..d8c0e1a
--- /dev/null
@@ -0,0 +1,574 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SearchDialog</class>
+ <widget class="QDialog" name="SearchDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>512</width>
+    <height>420</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Search Files</string>
+  </property>
+  <property name="windowIcon">
+   <iconset theme="system-search">
+    <normaloff/>
+   </iconset>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QTabWidget" name="tabWidget">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="tab">
+      <attribute name="title">
+       <string>Name/Location</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1">
+       <item>
+        <widget class="QGroupBox" name="groupBox">
+         <property name="title">
+          <string>File Name Patterns:</string>
+         </property>
+         <layout class="QVBoxLayout" name="verticalLayout_3">
+          <item>
+           <widget class="QLineEdit" name="namePatterns">
+            <property name="text">
+             <string>*</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QCheckBox" name="nameCaseInsensitive">
+            <property name="text">
+             <string>Case insensitive</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QCheckBox" name="nameRegExp">
+            <property name="text">
+             <string>Use regular expression</string>
+            </property>
+            <property name="checked">
+             <bool>true</bool>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="groupBox_2">
+         <property name="title">
+          <string>Places to Search:</string>
+         </property>
+         <layout class="QVBoxLayout" name="verticalLayout_5">
+          <item>
+           <layout class="QHBoxLayout" name="horizontalLayout">
+            <item>
+             <widget class="QListWidget" name="listView"/>
+            </item>
+            <item>
+             <layout class="QVBoxLayout" name="verticalLayout_4">
+              <item>
+               <widget class="QPushButton" name="addPath">
+                <property name="text">
+                 <string>&amp;Add</string>
+                </property>
+                <property name="icon">
+                 <iconset theme="list-add">
+                  <normaloff/>
+                 </iconset>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <widget class="QPushButton" name="removePath">
+                <property name="text">
+                 <string>&amp;Remove</string>
+                </property>
+                <property name="icon">
+                 <iconset theme="list-remove">
+                  <normaloff/>
+                 </iconset>
+                </property>
+               </widget>
+              </item>
+              <item>
+               <spacer name="verticalSpacer">
+                <property name="orientation">
+                 <enum>Qt::Vertical</enum>
+                </property>
+                <property name="sizeHint" stdset="0">
+                 <size>
+                  <width>20</width>
+                  <height>40</height>
+                 </size>
+                </property>
+               </spacer>
+              </item>
+             </layout>
+            </item>
+           </layout>
+          </item>
+          <item>
+           <widget class="QCheckBox" name="recursiveSearch">
+            <property name="text">
+             <string>Search in sub directories</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QCheckBox" name="searchHidden">
+            <property name="text">
+             <string>Search for hidden files</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tab_2">
+      <attribute name="title">
+       <string>File Type</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_7">
+       <item>
+        <widget class="QGroupBox" name="groupBox_3">
+         <property name="title">
+          <string>Only search for files of following types:</string>
+         </property>
+         <layout class="QVBoxLayout" name="verticalLayout_6">
+          <item>
+           <widget class="QCheckBox" name="searchTextFiles">
+            <property name="text">
+             <string>Text files</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QCheckBox" name="searchImages">
+            <property name="text">
+             <string>Image files</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QCheckBox" name="searchAudio">
+            <property name="text">
+             <string>Audio files</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QCheckBox" name="searchVideo">
+            <property name="text">
+             <string>Video files</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QCheckBox" name="searchDocuments">
+            <property name="text">
+             <string>Documents</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QCheckBox" name="searchFolders">
+            <property name="text">
+             <string>Folders</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tab_3">
+      <attribute name="title">
+       <string>Content</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_9">
+       <item>
+        <widget class="QGroupBox" name="groupBox_4">
+         <property name="title">
+          <string>File contains:</string>
+         </property>
+         <layout class="QVBoxLayout" name="verticalLayout_8">
+          <item>
+           <widget class="QLineEdit" name="contentPattern"/>
+          </item>
+          <item>
+           <widget class="QCheckBox" name="contentCaseInsensitive">
+            <property name="text">
+             <string>Case insensiti&amp;ve</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QCheckBox" name="contentRegExp">
+            <property name="text">
+             <string>&amp;Use regular expression</string>
+            </property>
+            <property name="checked">
+             <bool>true</bool>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_3">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>186</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="tab_4">
+      <attribute name="title">
+       <string>Properties</string>
+      </attribute>
+      <layout class="QVBoxLayout" name="verticalLayout_10">
+       <item>
+        <widget class="QGroupBox" name="groupBox_5">
+         <property name="title">
+          <string>File Size:</string>
+         </property>
+         <layout class="QFormLayout" name="formLayout">
+          <item row="0" column="0">
+           <layout class="QGridLayout" name="gridLayout_2">
+            <item row="1" column="0">
+             <widget class="QCheckBox" name="smallerThan">
+              <property name="text">
+               <string>Smaller than:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="2">
+             <widget class="QSpinBox" name="maxSize">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="2">
+             <widget class="QSpinBox" name="minSize">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="0">
+             <widget class="QCheckBox" name="largerThan">
+              <property name="text">
+               <string>Larger than:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="0" column="3">
+             <widget class="QComboBox" name="minSizeUnit">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="currentIndex">
+               <number>2</number>
+              </property>
+              <item>
+               <property name="text">
+                <string>Bytes</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>KiB</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>MiB</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>GiB</string>
+               </property>
+              </item>
+             </widget>
+            </item>
+            <item row="1" column="3">
+             <widget class="QComboBox" name="maxSizeUnit">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="currentIndex">
+               <number>2</number>
+              </property>
+              <item>
+               <property name="text">
+                <string>Bytes</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>KiB</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>MiB</string>
+               </property>
+              </item>
+              <item>
+               <property name="text">
+                <string>GiB</string>
+               </property>
+              </item>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <widget class="QGroupBox" name="groupBox_6">
+         <property name="layoutDirection">
+          <enum>Qt::LeftToRight</enum>
+         </property>
+         <property name="title">
+          <string>Last Modified Time:</string>
+         </property>
+         <layout class="QFormLayout" name="formLayout_2">
+          <item row="1" column="0">
+           <layout class="QGridLayout" name="gridLayout">
+            <item row="2" column="0">
+             <widget class="QCheckBox" name="earlierThan">
+              <property name="text">
+               <string>Earlier than:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="0">
+             <widget class="QCheckBox" name="laterThan">
+              <property name="text">
+               <string>Later than:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="1">
+             <widget class="QDateEdit" name="minTime">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="calendarPopup">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="1">
+             <widget class="QDateEdit" name="maxTime">
+              <property name="enabled">
+               <bool>false</bool>
+              </property>
+              <property name="calendarPopup">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item>
+        <spacer name="verticalSpacer_4">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>SearchDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>SearchDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>largerThan</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>minSizeUnit</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>93</x>
+     <y>84</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>403</x>
+     <y>88</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>smallerThan</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>maxSize</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>96</x>
+     <y>119</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>241</x>
+     <y>123</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>largerThan</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>minSize</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>93</x>
+     <y>84</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>241</x>
+     <y>88</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>smallerThan</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>maxSizeUnit</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>96</x>
+     <y>119</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>403</x>
+     <y>123</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>laterThan</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>minTime</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>88</x>
+     <y>223</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>319</x>
+     <y>226</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>earlierThan</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>maxTime</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>93</x>
+     <y>190</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>319</x>
+     <y>193</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/filesearchdialog.cpp b/src/filesearchdialog.cpp
new file mode 100644 (file)
index 0000000..2ee1af7
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "filesearchdialog.h"
+#include <QMessageBox>
+#include "fm-search.h"
+#include "ui_filesearch.h"
+#include <limits>
+#include <QFileDialog>
+#include <utility>
+
+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<int>().max());
+    ui->maxSize->setMaximum(std::numeric_limits<int>().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
new file mode 100644 (file)
index 0000000..5f6126b
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_FILESEARCHDIALOG_H
+#define FM_FILESEARCHDIALOG_H
+
+#include "libfmqtglobals.h"
+#include <QDialog>
+#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
new file mode 100644 (file)
index 0000000..4fa20d7
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * fm-search-uri.c
+ * 
+ * Copyright 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ * Copyright 2012-2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "fm-search.h"
+#include <string.h>
+
+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
new file mode 100644 (file)
index 0000000..f85b152
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * fm-search-uri.h
+ * 
+ * Copyright 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ * Copyright 2012-2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/* 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 <gio/gio.h>
+
+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/folderitemdelegate.cpp b/src/folderitemdelegate.cpp
new file mode 100644 (file)
index 0000000..a67c809
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "folderitemdelegate.h"
+#include "foldermodel.h"
+#include <QPainter>
+#include <QModelIndex>
+#include <QAbstractItemView>
+#include <QStyleOptionViewItem>
+#include <QApplication>
+#include <QIcon>
+#include <QTextLayout>
+#include <QTextOption>
+#include <QTextLine>
+#include <QLineEdit>
+#include <QTextEdit>
+#include <QTimer>
+#include <QDebug>
+
+namespace Fm {
+
+FolderItemDelegate::FolderItemDelegate(QAbstractItemView* view, QObject* parent):
+    QStyledItemDelegate(parent ? parent : view),
+    symlinkIcon_(QIcon::fromTheme("emblem-symbolic-link")),
+    untrustedIcon_(QIcon::fromTheme("emblem-important")),
+    addIcon_(QIcon::fromTheme("list-add")),
+    removeIcon_(QIcon::fromTheme("list-remove")),
+    fileInfoRole_(Fm::FolderModel::FileInfoRole),
+    iconInfoRole_(-1),
+    margins_(QSize(3, 3)),
+    shadowHidden_(false),
+    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<QSize>(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<std::shared_ptr<const Fm::IconInfo>> icon_emblems;
+    auto fmicon = index.data(iconInfoRole_).value<std::shared_ptr<const Fm::IconInfo>>();
+    if(fmicon) {
+        icon_emblems = fmicon->emblems();
+    }
+    // get file info for the item
+    auto file = index.data(fileInfoRole_).value<std::shared_ptr<const Fm::FileInfo>>();
+    const auto& emblems = file ? file->emblems() : icon_emblems;
+
+    QStyleOptionViewItem opt = option;
+    initStyleOption(&opt, index);
+
+    // distinguish the hidden items visually by making their texts italic
+    bool shadowIcon(false);
+    if(file && file->isHidden()) {
+        QFont f(opt.font);
+        f.setItalic(true);
+        opt.font = f;
+        shadowIcon = shadowHidden_;
+    }
+
+    bool isSymlink = file && file->isSymlink();
+    // for practical reasons, an emblem is added only to an untrusted, deletable desktop file
+    bool untrusted = file && !file->isTrustable() && file->isDesktopEntry() && file->isDeletable();
+    // 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 = shadowIcon ? QIcon::Disabled : 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));
+        }
+
+        if(untrusted) {
+            // emblem for untrusted, deletable desktop files
+            painter->drawPixmap(iconPos.x(), opt.rect.y() + option.decorationSize.height() / 2, untrustedIcon_.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 select/deselect icons outside the main icon but near its top left corner,
+        // with its 1/3 size and only if the icon size isn't smaller than 48 px
+        // (otherwise, the user could not click on them easily).
+        if(option.decorationSize.width() >= 48 && opt.state & QStyle::State_MouseOver) {
+            int s = option.decorationSize.width() / 3;
+            bool cursorOnSelectionCorner = false;
+            iconPos = QPoint(qMax(opt.rect.x(), iconPos.x() - s),
+                             qMax(opt.rect.y(), iconPos.y() - s));
+            if(const QAbstractItemView* iv = qobject_cast<const QAbstractItemView*>(opt.widget)) {
+                QPoint curPos = iv->viewport()->mapFromGlobal(QCursor::pos());
+                if(curPos.x() >= iconPos.x() && curPos.x() <= iconPos.x() + s
+                   && curPos.y() >= iconPos.y() && curPos.y() <= iconPos.y() + s) {
+                    cursorOnSelectionCorner = true;
+                }
+            }
+            if(!cursorOnSelectionCorner) { // make it translucent when not under the cursor
+                painter->save();
+                painter->setOpacity(0.6);
+            }
+            if(opt.state & QStyle::State_Selected) {
+                painter->drawPixmap(iconPos, removeIcon_.pixmap(QSize(s, s), QIcon::Normal));
+            }
+            else {
+                painter->drawPixmap(iconPos, addIcon_.pixmap(QSize(s, s), QIcon::Normal));
+            }
+            if(!cursorOnSelectionCorner) {
+                painter->restore();
+            }
+        }
+
+        // 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 the style engine do the painting but make the icon look disabled if shadowed.
+        // NOTE: The shadowing can also be done directly.
+        // WARNING: QStyledItemDelegate shouldn't be used for painting because it resets the icon.
+        // FIXME: For better text alignment, here we could have increased the icon width
+        // when it's smaller than the requested size.
+        if(shadowIcon) {
+            QPixmap pixmap = opt.icon.pixmap(option.decorationSize, QIcon::Disabled);
+            opt.icon = QIcon(pixmap);
+        }
+        const QWidget* widget = opt.widget;
+        QStyle* style = widget ? widget->style() : QApplication::style();
+        style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
+
+        // draw emblems if needed
+        QIcon::Mode iconMode = shadowIcon ? QIcon::Disabled : 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));
+        }
+        if(untrusted) {
+            QPoint iconPos(opt.rect.x(), opt.rect.y() + opt.rect.height() / 2);
+            painter->drawPixmap(iconPos, untrustedIcon_.pixmap(option.decorationSize / 2, iconMode));
+        }
+        if(!emblems.empty()) {
+            // 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::Text, qApp->palette().text().color());
+        p.setColor(QPalette::Base, qApp->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<QTextEdit*>(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<QLineEdit*>(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<QWidget*>(object);
+    if (editor && event->type() == QEvent::KeyPress) {
+        int k = static_cast<QKeyEvent *>(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
new file mode 100644 (file)
index 0000000..90756ed
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_FOLDERITEMDELEGATE_H
+#define FM_FOLDERITEMDELEGATE_H
+
+#include "libfmqtglobals.h"
+#include <QStyledItemDelegate>
+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_;
+    }
+
+    void setShadowHidden(bool value) {
+        shadowHidden_ = value;
+    }
+
+    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_;
+    QIcon untrustedIcon_;
+    QIcon addIcon_;
+    QIcon removeIcon_;
+    QSize iconSize_;
+    QSize itemSize_;
+    int fileInfoRole_;
+    int iconInfoRole_;
+    QColor shadowColor_;
+    QSize margins_;
+    bool shadowHidden_;
+    mutable bool hasEditor_;
+};
+
+}
+
+#endif // FM_FOLDERITEMDELEGATE_H
diff --git a/src/foldermenu.cpp b/src/foldermenu.cpp
new file mode 100644 (file)
index 0000000..1a87dd0
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ * Copyright (C) 2012 - 2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "foldermenu.h"
+#include "createnewmenu.h"
+#include "filepropsdialog.h"
+#include "folderview.h"
+#include "utilities.h"
+#include <cstring> // for memset
+#include <QDebug>
+#include "customaction_p.h"
+#include "customactions/fileaction.h"
+#include <QMessageBox>
+
+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<const FileActionItem> 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<CustomAction*>(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<QAction*>(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<QAction*>(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
new file mode 100644 (file)
index 0000000..d533c1d
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_FOLDERMENU_H
+#define FM_FOLDERMENU_H
+
+#include "libfmqtglobals.h"
+#include <QMenu>
+#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<const FileActionItem> 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
new file mode 100644 (file)
index 0000000..c453b5b
--- /dev/null
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "foldermodel.h"
+#include <iostream>
+#include <algorithm>
+#include <QtAlgorithms>
+#include <QVector>
+#include <qmimedata.h>
+#include <QMimeData>
+#include <QByteArray>
+#include <QPixmap>
+#include <QPainter>
+#include <QTimer>
+#include "utilities.h"
+#include "fileoperation.h"
+
+namespace Fm {
+
+FolderModel::FolderModel():
+    hasPendingThumbnailHandler_{false},
+    showFullNames_{false},
+    isLoaded_{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<Fm::Folder>& 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()) {
+            isLoaded_ = true;
+            insertFiles(0, folder_->files());
+        }
+    }
+}
+
+void FolderModel::onStartLoading() {
+    isLoaded_ = false;
+    // remove all items
+    removeAll();
+}
+
+void FolderModel::onFinishLoading() {
+    isLoaded_ = true;
+}
+
+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();
+
+    if(isLoaded_) {
+        Q_EMIT filesAdded(files);
+    }
+}
+
+void FolderModel::onFilesChanged(std::vector<Fm::FileInfoPair>& files) {
+    for(auto& change : files) {
+        int row;
+        auto& oldInfo = change.first;
+        auto& newInfo = change.second;
+        QList<FolderModelItem>::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<FolderModelItem>::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<const Fm::FileInfo>& 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<HashSet>();
+            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<FolderModelItem*>(index.internalPointer());
+}
+
+std::shared_ptr<const Fm::FileInfo> 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 (showFullNames_ && !item->name().empty() ? QString::fromStdString(item->name())
+                                                            : 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<FolderModelItem>::iterator FolderModel::findItemByPath(const Fm::FilePath& path, int* row) {
+    QList<FolderModelItem>::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<FolderModelItem>::iterator FolderModel::findItemByName(const char* name, int* row) {
+    QList<FolderModelItem>::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<FolderModelItem>::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<const Fm::FileInfo> 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<FolderModelItem>::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<Fm::ThumbnailJob*>(sender());
+    auto it = std::find(pendingThumbnailJobs_.cbegin(), pendingThumbnailJobs_.cend(), job);
+    if(it != pendingThumbnailJobs_.end()) {
+        pendingThumbnailJobs_.erase(it);
+    }
+}
+
+void FolderModel::onThumbnailLoaded(const std::shared_ptr<const Fm::FileInfo>& file, int size, const QImage& image) {
+    // find the model item this thumbnail belongs to
+    int row;
+    QList<FolderModelItem>::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
new file mode 100644 (file)
index 0000000..b8fc4a3
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_FOLDERMODEL_H
+#define FM_FOLDERMODEL_H
+
+#include "libfmqtglobals.h"
+#include <QAbstractListModel>
+#include <QItemSelection>
+#include <QIcon>
+#include <QImage>
+#include <QList>
+#include <vector>
+#include <utility>
+#include <forward_list>
+#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<Fm::Folder>& folder() const {
+        return folder_;
+    }
+
+    void setFolder(const std::shared_ptr<Fm::Folder>& 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<const Fm::FileInfo> 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);
+
+    void setShowFullName(bool fullName) {
+        showFullNames_ = fullName;
+    }
+
+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<Fm::FileInfoPair>& files);
+    void onFilesRemoved(const Fm::FileInfoList& files);
+
+    void onThumbnailLoaded(const std::shared_ptr<const Fm::FileInfo>& file, int size, const QImage& image);
+    void onThumbnailJobFinished();
+    void loadPendingThumbnails();
+
+protected:
+    void queueLoadThumbnail(const std::shared_ptr<const Fm::FileInfo>& file, int size);
+    void insertFiles(int row, const Fm::FileInfoList& files);
+    void removeAll();
+    QList<FolderModelItem>::iterator findItemByPath(const Fm::FilePath& path, int* row);
+    QList<FolderModelItem>::iterator findItemByName(const char* name, int* row);
+    QList<FolderModelItem>::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<Fm::Folder> folder_;
+    QList<FolderModelItem> items;
+
+    bool hasPendingThumbnailHandler_;
+    std::vector<Fm::ThumbnailJob*> pendingThumbnailJobs_;
+    std::forward_list<ThumbnailData> thumbnailData_;
+
+    bool showFullNames_;
+
+    bool isLoaded_;
+};
+
+}
+
+#endif // FM_FOLDERMODEL_H
diff --git a/src/foldermodelitem.cpp b/src/foldermodelitem.cpp
new file mode 100644 (file)
index 0000000..44922ce
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "foldermodelitem.h"
+#include <QDateTime>
+#include <QPainter>
+#include "utilities.h"
+#include "core/userinfocache.h"
+
+namespace Fm {
+
+FolderModelItem::FolderModelItem(const std::shared_ptr<const Fm::FileInfo>& _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<const HashSet>& 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<Thumbnail>::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<Thumbnail>::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
new file mode 100644 (file)
index 0000000..82eeb9a
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_FOLDERMODELITEM_H
+#define FM_FOLDERMODELITEM_H
+
+#include "libfmqtglobals.h"
+#include <QImage>
+#include <QString>
+#include <QIcon>
+#include <QVector>
+
+#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<const Fm::FileInfo>& _info);
+    FolderModelItem(const FolderModelItem& other);
+    virtual ~FolderModelItem();
+
+    const QString& displayName() const {
+        return info->displayName();
+    }
+
+    const std::string& name() const {
+        return info->name();
+    }
+
+    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<const HashSet>& cutFilesHashSet);
+
+    Thumbnail* findThumbnail(int size, bool transparent);
+
+    void removeThumbnail(int size);
+
+    std::shared_ptr<const Fm::FileInfo> info;
+    mutable QString dispMtime_;
+    mutable QString dispSize_;
+    std::weak_ptr<const HashSet> cutFilesHashSet_;
+    QVector<Thumbnail> thumbnails;
+};
+
+}
+
+#endif // FM_FOLDERMODELITEM_H
diff --git a/src/folderview.cpp b/src/folderview.cpp
new file mode 100644 (file)
index 0000000..d1930e8
--- /dev/null
@@ -0,0 +1,1445 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "folderview.h"
+#include "foldermodel.h"
+#include <QHeaderView>
+#include <QVBoxLayout>
+#include <QContextMenuEvent>
+#include "proxyfoldermodel.h"
+#include "folderitemdelegate.h"
+#include "dndactionmenu.h"
+#include "filemenu.h"
+#include "foldermenu.h"
+#include "filelauncher.h"
+#include "utilities.h"
+#include <QTimer>
+#include <QDate>
+#include <QDebug>
+#include <QClipboard>
+#include <QMimeData>
+#include <QHoverEvent>
+#include <QApplication>
+#include <QScrollBar>
+#include <QMetaType>
+#include <QMessageBox>
+#include <QLineEdit>
+#include <QTextEdit>
+#include <QX11Info> // for XDS support
+#include <xcb/xcb.h> // for XDS support
+#include "xdndworkaround.h" // for XDS support
+#include "folderview_p.h"
+#include "utilities.h"
+
+#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),
+    cursorOnSelectionCorner_(false) {
+    connect(this, &QListView::activated, this, &FolderViewListView::activation);
+    // inline renaming
+    setEditTriggers(QAbstractItemView::NoEditTriggers);
+    setMouseTracking(true); // needed with selection corner icon
+}
+
+FolderViewListView::~FolderViewListView() {
+}
+
+void FolderViewListView::startDrag(Qt::DropActions supportedActions) {
+    if(movement() != Static) {
+        QListView::startDrag(supportedActions);
+    }
+    else {
+        QAbstractItemView::startDrag(supportedActions);
+    }
+}
+
+void FolderViewListView::mousePressEvent(QMouseEvent* event) {
+    setSelectionMode(cursorOnSelectionCorner_ && event->button() == Qt::LeftButton
+                         ? QAbstractItemView::MultiSelection
+                         : QAbstractItemView::ExtendedSelection);
+    QListView::mousePressEvent(event);
+    static_cast<FolderView*>(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)) {
+        bool cursorOnSelectionCorner = cursorOnSelectionCorner_;
+        QListView::mouseMoveEvent(event);
+        // update the index if the cursor enters/leaves the selection corner icon
+        if(cursorOnSelectionCorner != cursorOnSelectionCorner_ && event->buttons() == Qt::NoButton) {
+            update(indexAt(event->pos()));
+        }
+    }
+}
+
+QModelIndex FolderViewListView::indexAt(const QPoint& point) const {
+    QModelIndex index = QListView::indexAt(point);
+    bool isCursorPos(point == viewport()->mapFromGlobal(QCursor::pos()));
+    if(isCursorPos) {
+        cursorOnSelectionCorner_ = false;
+    }
+    // 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<FolderItemDelegate*>(itemDelegateForColumn(FolderModel::ColumnFileName));
+        QSize margins = delegate->getMargins();
+        QSize _iconSize = iconSize();
+        int iconXMargin = (visRect.width() - _iconSize.width()) / 2;
+        int iconLeft = visRect.left() + iconXMargin;
+        int iconTop = visRect.top() + margins.height();
+        // the selection (hover) corner is a rectangle near the top left corner of
+        // the icon and outside it as far as possible, so that its width and height
+        // are 1/3 of the icon size >= 48 px (see FolderItemDelegate::paint)
+        if(isCursorPos && _iconSize.width() >= 48) {
+            int s = _iconSize.width() / 3;
+            iconLeft = qMax(visRect.left(), iconLeft - s);
+            iconTop = qMax(visRect.top(), iconTop - s);
+            if(point.x() >= iconLeft &&  point.x() <= iconLeft + s
+               && point.y() >= iconTop &&  point.y() <= iconTop + s) {
+                cursorOnSelectionCorner_ = true;
+                return index;
+            }
+        }
+        if(point.y() < iconTop) { // above icon
+            return QModelIndex();
+        }
+        else if(point.y() < visRect.top() + margins.height() + _iconSize.height()) { // on the icon area
+            if(point.x() < iconLeft || 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<FolderView*>(parent())->childDragEnterEvent(event);
+}
+
+void FolderViewListView::dragLeaveEvent(QDragLeaveEvent* e) {
+    if(movement() != Static) {
+        QListView::dragLeaveEvent(e);
+    }
+    else {
+        QAbstractItemView::dragLeaveEvent(e);
+    }
+    static_cast<FolderView*>(parent())->childDragLeaveEvent(e);
+}
+
+void FolderViewListView::dragMoveEvent(QDragMoveEvent* e) {
+    if(movement() != Static) {
+        QListView::dragMoveEvent(e);
+    }
+    else {
+        QAbstractItemView::dragMoveEvent(e);
+    }
+    static_cast<FolderView*>(parent())->childDragMoveEvent(e);
+}
+
+void FolderViewListView::dropEvent(QDropEvent* e) {
+
+    static_cast<FolderView*>(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
+       // no activation with mouse when the cursor is on the selection corner
+       || cursorOnSelectionCorner_) {
+        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
+       // no activation with mouse when the cursor is on the selection corner
+       || cursorOnSelectionCorner_) {
+        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<FolderView*>(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<ProxyFolderModel*>(model)) {
+        connect(proxyModel, &ProxyFolderModel::sortFilterChanged, this, &FolderViewTreeView::onSortFilterChanged,
+                Qt::UniqueConnection);
+        onSortFilterChanged();
+    }
+}
+
+void FolderViewTreeView::mousePressEvent(QMouseEvent* event) {
+    QTreeView::mousePressEvent(event);
+    static_cast<FolderView*>(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<FolderView*>(parent())->childDragEnterEvent(event);
+}
+
+void FolderViewTreeView::dragLeaveEvent(QDragLeaveEvent* e) {
+    QTreeView::dragLeaveEvent(e);
+    static_cast<FolderView*>(parent())->childDragLeaveEvent(e);
+}
+
+void FolderViewTreeView::dragMoveEvent(QDragMoveEvent* e) {
+    QTreeView::dragMoveEvent(e);
+    static_cast<FolderView*>(parent())->childDragMoveEvent(e);
+}
+
+void FolderViewTreeView::dropEvent(QDropEvent* e) {
+    static_cast<FolderView*>(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<int>& roles /*= QVector<int>{}*/) {
+    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<QSortFilterProxyModel*>(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)),
+    shadowHidden_(false),
+    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) {
+    QItemSelectionModel* selModel = selectionModel();
+    if(index.isValid() && index.model()
+       && selModel && selModel->isSelected(index)) { // do nothing when the item is not selected
+        QVariant data = index.model()->data(index, FolderModel::FileInfoRole);
+        auto info = data.value<std::shared_ptr<const Fm::FileInfo>>();
+        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<QTextEdit*>(editor)) { // icon and thumbnail view
+        newName = qobject_cast<QTextEdit*>(editor)->toPlainText();
+    }
+    else if (qobject_cast<QLineEdit*>(editor)) { // compact view
+        newName = qobject_cast<QLineEdit*>(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<std::shared_ptr<const Fm::FileInfo>>();
+        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);
+        delegate->setShadowHidden(shadowHidden_);
+        treeView->setItemDelegateForColumn(FolderModel::ColumnFileName, delegate);
+    }
+    else {
+        FolderViewListView* listView;
+        if(view) {
+            listView = static_cast<FolderViewListView*>(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);
+        delegate->setShadowHidden(shadowHidden_);
+        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<FolderViewListView*>(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<FolderItemDelegate*>(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();
+    }
+}
+
+void FolderView::setShadowHidden(bool shadowHidden) {
+    if(view && shadowHidden != shadowHidden_) {
+        shadowHidden_ = shadowHidden;
+        FolderItemDelegate* delegate = nullptr;
+        if(mode == DetailedListMode) {
+            FolderViewTreeView* treeView = static_cast<FolderViewTreeView*>(view);
+            delegate = static_cast<FolderItemDelegate*>(treeView->itemDelegateForColumn(FolderModel::ColumnFileName));
+        }
+        else {
+            FolderViewListView* listView = static_cast<FolderViewListView*>(view);
+            delegate = static_cast<FolderItemDelegate*>(listView->itemDelegateForColumn(FolderModel::ColumnFileName));
+        }
+        if(delegate) {
+            delegate->setShadowHidden(shadowHidden);
+        }
+    }
+}
+
+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<std::shared_ptr<const Fm::FileInfo>>();
+        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);
+    QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Select;
+    if(mode == DetailedListMode) {
+        flags |= QItemSelectionModel::Rows;
+    }
+    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, flags);
+                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();
+        QItemSelectionModel::SelectionFlags flags;
+        if(mode == DetailedListMode) {
+            flags |= QItemSelectionModel::Rows;
+        }
+        // we don't use a "for" loop on rows because it would be slow
+        const QItemSelection _all{model_->index(0, 0), model_->index(model_->rowCount() - 1, 0)};
+        const QItemSelection _old{selModel->selection()};
+        
+        selModel->select(_all, QItemSelectionModel::Select);
+        selModel->select(_old, QItemSelectionModel::Deselect);
+    }
+}
+
+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<std::shared_ptr<const Fm::FileInfo>>();
+        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<QHoverEvent*>(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<FolderViewTreeView*>(view);
+                    delegate = static_cast<FolderItemDelegate*>(treeView->itemDelegateForColumn(FolderModel::ColumnFileName));
+                }
+                else {
+                    FolderViewListView* listView = static_cast<FolderViewListView*>(view);
+                    delegate = static_cast<FolderItemDelegate*>(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 <https://github.com/zhou13/qsmoothscrollarea>.
+            else if(mode != DetailedListMode
+                    && event->spontaneous()
+                    && !(QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::AltModifier))) {
+                if(QScrollBar* vbar = view->verticalScrollBar()) {
+                    // keep track of the wheel event for smooth scrolling
+                    wheelEvent_ = static_cast<QWheelEvent*>(event);
+                    int delta = wheelEvent_->angleDelta().y();
+                    if((delta > 0 && vbar->value() == vbar->minimum()) || (delta < 0 && vbar->value() == vbar->maximum())) {
+                        break; // the scrollbar can't move
+                    }
+                    // get a rough estimation of the wheel speed and disable animation if it's too high
+                    static QList<qint64> wheelEvents;
+                    wheelEvents << QDateTime::currentMSecsSinceEpoch();
+                    while(wheelEvents.last() - wheelEvents.first() > 500) {
+                        wheelEvents.removeFirst();
+                    }
+                    if(wheelEvents.size() > 10) {
+                        break;
+                    }
+
+                    if(!smoothScrollTimer_) {
+                        smoothScrollTimer_ = new QTimer();
+                        connect(smoothScrollTimer_, &QTimer::timeout, this, &FolderView::scrollSmoothly);
+                    }
+
+                    // set the data for smooth scrolling
+                    scollData data;
+                    data.delta = delta;
+                    data.leftFrames = scrollAnimFrames;
+                    queuedScrollSteps_.append(data);
+                    smoothScrollTimer_->start(1000 / SCROLL_FRAMES_PER_SEC);
+                    return true;
+                }
+            }
+            break;
+        default:
+            break;
+        }
+    }
+    return QObject::eventFilter(watched, event);
+}
+
+void FolderView::scrollSmoothly() {
+    if(!wheelEvent_ || !view->verticalScrollBar()) {
+        return;
+    }
+
+    int totalDelta = 0;
+    QList<scollData>::iterator it = queuedScrollSteps_.begin();
+    while(it != queuedScrollSteps_.end()) {
+        if(it->leftFrames == 1) { // find the exact delta for the last frame
+            totalDelta += it->delta - (scrollAnimFrames - 1) * qRound((qreal)it->delta / (qreal)scrollAnimFrames);
+            it = queuedScrollSteps_.erase(it);
+        }
+        else {
+            totalDelta += qRound((qreal)it->delta / (qreal)scrollAnimFrames);
+            -- it->leftFrames;
+            ++it;
+        }
+    }
+    if(totalDelta != 0) {
+        // as in qevent.cpp -> QWheelEvent::QWheelEvent()
+        QWheelEvent e(wheelEvent_->pos(), wheelEvent_->globalPos(),
+                      totalDelta,
+                      wheelEvent_->buttons(), Qt::NoModifier, Qt::Vertical);
+        QApplication::sendEvent(view->verticalScrollBar(), &e);
+    }
+    if(queuedScrollSteps_.empty()) {
+        smoothScrollTimer_->stop();
+    }
+}
+
+// this slot handles auto-selection of items.
+void FolderView::onAutoSelectionTimeout() {
+    if(QApplication::mouseButtons() != Qt::NoButton) {
+        return;
+    }
+
+    // don't do anything if the cursor is on selection corner icon
+    if(mode != DetailedListMode) {
+        FolderViewListView* listView = static_cast<FolderViewListView*>(view);
+        if(listView->cursorOnSelectionCorner()) {
+            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<const Fm::FileInfo> &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()) {
+                Fm::FileMenu* fileMenu = (view && files.size() == 1)
+                                         ? new Fm::FileMenu(files, fileInfo, folderPath, isWritableDir, QString(), view)
+                                         : new Fm::FileMenu(files, fileInfo, folderPath, isWritableDir);
+                fileMenu->setFileLauncher(fileLauncher_);
+                fileMenu->addTrustAction();
+                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<HashSet>()); // 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
new file mode 100644 (file)
index 0000000..1339893
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_FOLDERVIEW_H
+#define FM_FOLDERVIEW_H
+
+#include "libfmqtglobals.h"
+#include <QWidget>
+#include <QListView>
+#include <QTreeView>
+#include <QMouseEvent>
+#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<Fm::Folder> folder() const {
+        return model_ ? static_cast<FolderModel*>(model_->sourceModel())->folder() : nullptr;
+    }
+
+    std::shared_ptr<const Fm::FileInfo> 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);
+
+    void setShadowHidden(bool shadowHidden);
+
+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<const Fm::FileInfo>& 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<const Fm::FileInfo>& 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_;
+    bool shadowHidden_;
+    // smooth scrolling:
+    struct scollData {
+        int delta;
+        int leftFrames;
+    };
+    QTimer *smoothScrollTimer_;
+    QWheelEvent *wheelEvent_;
+    QList<scollData> queuedScrollSteps_;
+};
+
+}
+
+#endif // FM_FOLDERVIEW_H
diff --git a/src/folderview_p.h b/src/folderview_p.h
new file mode 100644 (file)
index 0000000..e92be89
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_FOLDERVIEW_P_H
+#define FM_FOLDERVIEW_P_H
+
+#include <QListView>
+#include <QTreeView>
+#include <QMouseEvent>
+#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();
+  }
+
+  inline bool cursorOnSelectionCorner() const {
+      return cursorOnSelectionCorner_;
+  }
+
+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_;
+  mutable bool cursorOnSelectionCorner_;
+};
+
+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<int>& roles = QVector<int>{});
+  virtual void reset();
+
+  virtual void resizeEvent(QResizeEvent* event);
+  void queueLayoutColumns();
+
+  virtual void keyboardSearch(const QString &search) {
+    QAbstractItemView::keyboardSearch(search); // let items be selected by typing
+  }
+
+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
new file mode 100644 (file)
index 0000000..f3b21a6
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "fontbutton.h"
+#include <QFontDialog>
+#include <X11/X.h>
+
+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
new file mode 100644 (file)
index 0000000..4dee7b2
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_FONTBUTTON_H
+#define FM_FONTBUTTON_H
+
+#include "libfmqtglobals.h"
+#include <QPushButton>
+
+
+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
new file mode 100644 (file)
index 0000000..793fb13
--- /dev/null
@@ -0,0 +1,12 @@
+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.
+URL: https://github.com/lxqt/libfm-qt
+Requires: @REQUIRED_QT@
+Version: @LIBFM_QT_API_VERSION@
+Libs: -L${libdir} -l@LIBFM_QT_LIBRARY_NAME@
+Cflags: -I${includedir}
diff --git a/src/libfmqt.cpp b/src/libfmqt.cpp
new file mode 100644 (file)
index 0000000..83905ee
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "libfmqt.h"
+#include <QLocale>
+#include <QPixmapCache>
+#include "core/thumbnailer.h"
+#include "xdndworkaround.h"
+#include "core/vfs/fm-file.h"
+#include "core/legacy/fm-config.h"
+
+namespace Fm {
+
+struct LibFmQtData {
+    LibFmQtData();
+    ~LibFmQtData();
+
+    QTranslator translator;
+    XdndWorkaround workaround;
+    int refCount;
+    Q_DISABLE_COPY(LibFmQtData)
+};
+
+static LibFmQtData* theLibFmData = nullptr;
+
+extern "C" {
+
+GFile *_fm_vfs_search_new_for_uri(const char *uri);  // defined in vfs-search.c
+GFile *_fm_vfs_menu_new_for_uri(const char *uri);  // defined in vfs-menu.c
+
+}
+
+static GFile* lookupSearchUri(GVfs * /*vfs*/, const char *identifier, gpointer /*user_data*/) {
+    return _fm_vfs_search_new_for_uri(identifier);
+}
+
+static GFile* lookupMenuUri(GVfs * /*vfs*/, const char *identifier, gpointer /*user_data*/) {
+    return _fm_vfs_menu_new_for_uri(identifier);
+}
+
+LibFmQtData::LibFmQtData(): refCount(1) {
+#if !GLIB_CHECK_VERSION(2, 36, 0)
+    g_type_init();
+#endif
+    // 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");
+
+    // FIXME: we keep the FmConfig data structure here to keep compatibility with legacy libfm API.
+    fm_config_init();
+
+    // register some URI schemes implemented by libfm
+    GVfs* vfs = g_vfs_get_default();
+    g_vfs_register_uri_scheme(vfs, "menu", lookupMenuUri, nullptr, nullptr, lookupMenuUri, nullptr, nullptr);
+    g_vfs_register_uri_scheme(vfs, "search", lookupSearchUri, nullptr, nullptr, lookupSearchUri, nullptr, nullptr);
+}
+
+LibFmQtData::~LibFmQtData() {
+    // _fm_file_finalize();
+
+    GVfs* vfs = g_vfs_get_default();
+    g_vfs_unregister_uri_scheme(vfs, "menu");
+    g_vfs_unregister_uri_scheme(vfs, "search");
+}
+
+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
new file mode 100644 (file)
index 0000000..a186064
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_APPLICATION_H
+#define FM_APPLICATION_H
+
+#include "libfmqtglobals.h"
+#include <QtGlobal>
+#include <QTranslator>
+
+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
new file mode 100644 (file)
index 0000000..1a6d4fe
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef _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
new file mode 100644 (file)
index 0000000..72f1b30
--- /dev/null
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MountOperationPasswordDialog</class>
+ <widget class="QDialog" name="MountOperationPasswordDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>244</width>
+    <height>302</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>Mount</string>
+  </property>
+  <property name="windowIcon">
+   <iconset theme="dialog-password"/>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>false</bool>
+  </property>
+  <property name="modal">
+   <bool>false</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <widget class="QLabel" name="message">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QRadioButton" name="Anonymous">
+     <property name="text">
+      <string>Connect &amp;anonymously</string>
+     </property>
+     <attribute name="buttonGroup">
+      <string notr="true">usernameGroup</string>
+     </attribute>
+    </widget>
+   </item>
+   <item>
+    <widget class="QRadioButton" name="asUser">
+     <property name="text">
+      <string>Connect as u&amp;ser:</string>
+     </property>
+     <attribute name="buttonGroup">
+      <string notr="true">usernameGroup</string>
+     </attribute>
+    </widget>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="1">
+      <widget class="QLineEdit" name="username"/>
+     </item>
+     <item row="0" column="0">
+      <widget class="QLabel" name="label">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>&amp;Username:</string>
+       </property>
+       <property name="buddy">
+        <cstring>username</cstring>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="1">
+      <widget class="QLineEdit" name="password">
+       <property name="echoMode">
+        <enum>QLineEdit::Password</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="3" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>&amp;Password:</string>
+       </property>
+       <property name="buddy">
+        <cstring>password</cstring>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="domainLabel">
+       <property name="text">
+        <string>&amp;Domain:</string>
+       </property>
+       <property name="buddy">
+        <cstring>domain</cstring>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLineEdit" name="domain"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QRadioButton" name="forgetPassword">
+     <property name="text">
+      <string>Forget password &amp;immediately</string>
+     </property>
+     <attribute name="buttonGroup">
+      <string notr="true">passwordGroup</string>
+     </attribute>
+    </widget>
+   </item>
+   <item>
+    <widget class="QRadioButton" name="sessionPassword">
+     <property name="text">
+      <string>Remember password until you &amp;logout</string>
+     </property>
+     <attribute name="buttonGroup">
+      <string notr="true">passwordGroup</string>
+     </attribute>
+    </widget>
+   </item>
+   <item>
+    <widget class="QRadioButton" name="storePassword">
+     <property name="text">
+      <string>Remember &amp;forever</string>
+     </property>
+     <attribute name="buttonGroup">
+      <string notr="true">passwordGroup</string>
+     </attribute>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>Anonymous</tabstop>
+  <tabstop>asUser</tabstop>
+  <tabstop>username</tabstop>
+  <tabstop>domain</tabstop>
+  <tabstop>password</tabstop>
+  <tabstop>forgetPassword</tabstop>
+  <tabstop>sessionPassword</tabstop>
+  <tabstop>storePassword</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>MountOperationPasswordDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>MountOperationPasswordDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+ <buttongroups>
+  <buttongroup name="usernameGroup"/>
+  <buttongroup name="passwordGroup"/>
+ </buttongroups>
+</ui>
diff --git a/src/mountoperation.cpp b/src/mountoperation.cpp
new file mode 100644 (file)
index 0000000..09a8df2
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "mountoperation.h"
+#include <glib/gi18n.h> // for _()
+#include <glib/gstdio.h> // for g_chdir()
+#include <QMessageBox>
+#include <QPushButton>
+#include <QEventLoop>
+#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<MountOperation>(this));
+}
+
+void MountOperation::mountMountable(const FilePath &mountable) {
+    g_file_mount_mountable(mountable.gfile().get(), G_MOUNT_MOUNT_NONE, op, cancellable_,
+                           (GAsyncReadyCallback)onMountMountableFinished, new QPointer<MountOperation>(this));
+}
+
+void MountOperation::onAbort(GMountOperation* /*_op*/, MountOperation* /*pThis*/) {
+
+}
+
+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<MountOperation>* pThis) {
+    if(*pThis) {
+        GError* error = nullptr;
+        g_file_mount_mountable_finish(file, res, &error);
+        (*pThis)->handleFinish(error);
+    }
+    delete pThis;
+}
+
+void MountOperation::onMountVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer< MountOperation >* pThis) {
+    if(*pThis) {
+        GError* error = nullptr;
+        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<void *>(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
new file mode 100644 (file)
index 0000000..67219b9
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_MOUNTOPERATION_H
+#define FM_MOUNTOPERATION_H
+
+#include "libfmqtglobals.h"
+#include <QWidget>
+#include <QDialog>
+#include <gio/gio.h>
+#include <QPointer>
+
+#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<MountOperation>(this));
+    }
+
+    void unmount(GMount* mount) {
+        prepareUnmount(mount);
+        g_mount_unmount_with_operation(mount, G_MOUNT_UNMOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onUnmountMountFinished, new QPointer<MountOperation>(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<MountOperation>(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<MountOperation>(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<MountOperation>* pThis);
+    static void onMountMountableFinished(GFile* file, GAsyncResult* res, QPointer<MountOperation>* pThis);
+    static void onMountVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer<MountOperation>* pThis);
+    static void onUnmountMountFinished(GMount* mount, GAsyncResult* res, QPointer<MountOperation>* pThis);
+    static void onEjectMountFinished(GMount* mount, GAsyncResult* res, QPointer<MountOperation>* pThis);
+    static void onEjectVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer<MountOperation>* 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
new file mode 100644 (file)
index 0000000..4e7d850
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "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
new file mode 100644 (file)
index 0000000..1fd6cd1
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_MOUNTOPERATIONPASSWORDDIALOG_H
+#define FM_MOUNTOPERATIONPASSWORDDIALOG_H
+
+#include "libfmqtglobals.h"
+#include <QDialog>
+#include <gio/gio.h>
+
+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
new file mode 100644 (file)
index 0000000..3679740
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "mountoperationquestiondialog_p.h"
+#include "mountoperation.h"
+#include <QPushButton>
+
+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
new file mode 100644 (file)
index 0000000..8c6a810
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_MOUNTOPERATIONQUESTIONDIALOG_H
+#define FM_MOUNTOPERATIONQUESTIONDIALOG_H
+
+#include "libfmqtglobals.h"
+#include <QCloseEvent>
+#include <QMessageBox>
+#include <gio/gio.h>
+
+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
new file mode 100644 (file)
index 0000000..c498c31
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2016  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "pathbar.h"
+#include "pathbar_p.h"
+#include <QToolButton>
+#include <QScrollArea>
+#include <QScrollBar>
+#include <QHBoxLayout>
+#include <QResizeEvent>
+#include <QContextMenuEvent>
+#include <QMenu>
+#include <QClipboard>
+#include <QApplication>
+#include <QTimer>
+#include <QDebug>
+#include "pathedit.h"
+
+
+namespace Fm {
+
+PathBar::PathBar(QWidget* parent):
+    QWidget(parent),
+    tempPathEdit_(nullptr),
+    toggledBtn_(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();
+    QTimer::singleShot(0, this, SLOT(ensureToggledVisible()));
+}
+
+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<PathButton*>(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<PathButton*>(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<PathButton*>(sender());
+        toggledBtn_ = btn;
+        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() {
+    if(toggledBtn_ != nullptr && tempPathEdit_ == nullptr) {
+        scrollArea_->ensureWidgetVisible(toggledBtn_, 1);
+    }
+}
+
+void PathBar::onScrollButtonClicked() {
+    QToolButton* btn = static_cast<QToolButton*>(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<PathButton*>(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);
+    toggledBtn_ = nullptr;
+    // 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<PathButton*>(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
new file mode 100644 (file)
index 0000000..c0dd464
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_PATHBAR_H
+#define FM_PATHBAR_H
+
+#include "libfmqtglobals.h"
+#include <QWidget>
+#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
+    PathButton* toggledBtn_;
+};
+
+} // namespace Fm
+
+#endif // FM_PATHBAR_H
diff --git a/src/pathbar_p.h b/src/pathbar_p.h
new file mode 100644 (file)
index 0000000..6a64b98
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_PATHBAR_P_H
+#define FM_PATHBAR_P_H
+
+#include <QToolButton>
+#include <QStyle>
+#include <QEvent>
+#include <QMouseEvent>
+#include <QString>
+#include <string>
+
+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
new file mode 100644 (file)
index 0000000..b8495ee
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "pathedit.h"
+#include "pathedit_p.h"
+#include <QCompleter>
+#include <QStringListModel>
+#include <QStringBuilder>
+#include <QThread>
+#include <QDebug>
+#include <QKeyEvent>
+#include <QDir>
+
+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) {
+    completer_->setCaseSensitivity(Qt::CaseInsensitive);
+    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<QKeyEvent*>(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 = g_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<PathEditJob*>(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
new file mode 100644 (file)
index 0000000..6385ddf
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_PATHEDIT_H
+#define FM_PATHEDIT_H
+
+#include "libfmqtglobals.h"
+#include <QLineEdit>
+#include <gio/gio.h>
+
+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
new file mode 100644 (file)
index 0000000..190f0bc
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_PATHEDIT_P_H
+#define FM_PATHEDIT_P_H
+
+#include <QObject>
+
+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
new file mode 100644 (file)
index 0000000..7494c99
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "placesmodel.h"
+#include <gio/gio.h>
+#include <QDebug>
+#include <QMimeData>
+#include <QTimer>
+#include <QPointer>
+#include <QStandardPaths>
+#include "utilities.h"
+#include "placesmodelitem.h"
+
+namespace Fm {
+
+std::weak_ptr<PlacesModel> PlacesModel::globalInstance_;
+
+PlacesModel::PlacesModel(QObject* parent):
+    QStandardItemModel(parent),
+    showApplications_(true),
+    showDesktop_(true),
+    trashMonitor_(nullptr),
+    trashUpdateTimer_(nullptr),
+    // 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) {
+    if(pThis->trashUpdateTimer_ != nullptr && !pThis->trashUpdateTimer_->isActive()) {
+        pThis->trashUpdateTimer_->start(250); // don't update trash very fast
+    }
+}
+
+void PlacesModel::updateTrash() {
+
+    struct UpdateTrashData {
+        QPointer<PlacesModel> model;
+        GFile* gf;
+        UpdateTrashData(PlacesModel* _model) : model(_model) {
+            gf = g_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<UpdateTrashData*>(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 = g_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_ = g_file_monitor_directory(gf, G_FILE_MONITOR_NONE, nullptr, nullptr);
+    if(trashMonitor_) {
+        if(trashUpdateTimer_ == nullptr) {
+            trashUpdateTimer_ = new QTimer(this);
+            trashUpdateTimer_->setSingleShot(true);
+            connect(trashUpdateTimer_, &QTimer::timeout, this, &PlacesModel::updateTrash);
+        }
+        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(trashUpdateTimer_) {
+                trashUpdateTimer_->stop();
+                delete trashUpdateTimer_;
+                trashUpdateTimer_ = nullptr;
+            }
+            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<PlacesModelItem*>(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<PlacesModelItem*>(devicesRoot->child(i, 0));
+        if(item->type() == PlacesModelItem::Volume) {
+            PlacesModelVolumeItem* volumeItem = static_cast<PlacesModelVolumeItem*>(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<PlacesModelItem*>(devicesRoot->child(i, 0));
+        if(item->type() == PlacesModelItem::Mount) {
+            PlacesModelMountItem* mountItem = static_cast<PlacesModelMountItem*>(item);
+            if(mountItem->mount() == mount) {
+                return mountItem;
+            }
+        }
+    }
+    return nullptr;
+}
+
+PlacesModelBookmarkItem* PlacesModel::itemFromBookmark(std::shared_ptr<const Fm::BookmarkItem> bkitem) {
+    int rowCount = bookmarksRoot->rowCount();
+    for(int i = 0; i < rowCount; ++i) {
+        PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(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<QStandardItem*>() << 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<QStandardItem*>() << volumeItem << ejectBtn);
+    }
+}
+
+void PlacesModel::onVolumeChanged(GVolumeMonitor* /*monitor*/, GVolume* volume, PlacesModel* pThis) {
+    PlacesModelVolumeItem* item = pThis->itemFromVolume(volume);
+    if(item) {
+        item->update();
+        QStandardItem* ejectBtn = item->parent()->child(item->row(), 1);
+        Q_ASSERT(ejectBtn);
+        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)
+            ejectBtn->setIcon(QIcon());
+        }
+        else if(ejectBtn->icon().isNull()) {
+            // this function may be called before onMountAdded(),
+            // so that the path is set but the eject icon isn't added yet
+            ejectBtn->setIcon(pThis->ejectIcon_);
+        }
+    }
+}
+
+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<PlacesModelItem*>(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> PlacesModel::globalInstance() {
+    auto model = globalInstance_.lock();
+    if(!model) {
+        model = std::make_shared<PlacesModel>();
+        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 <oldRow>, 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<PlacesModelItem*>(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<PlacesModelBookmarkItem*>(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
new file mode 100644 (file)
index 0000000..a18f804
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_PLACESMODEL_H
+#define FM_PLACESMODEL_H
+
+#include "libfmqtglobals.h"
+#include <QStandardItemModel>
+#include <QStandardItem>
+#include <QList>
+#include <QAction>
+
+#include <memory>
+
+#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<PlacesModel> 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<const Fm::BookmarkItem> 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<Fm::Bookmarks> bookmarks;
+    GVolumeMonitor* volumeMonitor;
+    bool showApplications_;
+    bool showDesktop_;
+    QStandardItem* placesRoot;
+    QStandardItem* devicesRoot;
+    QStandardItem* bookmarksRoot;
+    PlacesModelItem* trashItem_;
+    GFileMonitor* trashMonitor_;
+    QTimer* trashUpdateTimer_;
+    PlacesModelItem* desktopItem;
+    PlacesModelItem* homeItem;
+    PlacesModelItem* computerItem;
+    PlacesModelItem* networkItem;
+    PlacesModelItem* applicationsItem;
+    QIcon ejectIcon_;
+    QList<GMount*> shadowedMounts_;
+
+    static std::weak_ptr<PlacesModel> globalInstance_;
+};
+
+}
+
+#endif // FM_PLACESMODEL_H
diff --git a/src/placesmodelitem.cpp b/src/placesmodelitem.cpp
new file mode 100644 (file)
index 0000000..9f178a8
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "placesmodelitem.h"
+#include <gio/gio.h>
+#include <QPainter>
+
+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<const Fm::IconInfo> 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<const Fm::IconInfo> 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<const Fm::BookmarkItem> 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<GVolume*>(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());
+
+    QString toolTip;
+
+    // set dir path and tooltip
+    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);
+        toolTip = mount_root.toString().get();
+    }
+    else {
+        setPath(Fm::FilePath{});
+        if(CStrPtr identifier{g_volume_get_identifier(volume_, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE)}) {
+            toolTip = QObject::tr("Identifier: ");
+            toolTip += identifier.get();
+        }
+        if(CStrPtr uuid{g_volume_get_uuid(volume_)}) {
+            if(toolTip.isEmpty()) {
+                toolTip = "UUID: ";
+            }
+            else {
+                toolTip += "\nUUID: ";
+            }
+            toolTip += uuid.get();
+        }
+    }
+
+    setToolTip(toolTip);
+}
+
+
+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<GMount*>(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);
+    setToolTip(mount_root.toString().get());
+
+    // set icon
+    Fm::GIconPtr gicon{g_mount_get_icon(mount_), false};
+    setIcon(gicon.get());
+}
+
+}
diff --git a/src/placesmodelitem.h b/src/placesmodelitem.h
new file mode 100644 (file)
index 0000000..3ecd9bc
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_PLACESMODELITEM_H
+#define FM_PLACESMODELITEM_H
+
+#include "libfmqtglobals.h"
+#include <QStandardItemModel>
+#include <QStandardItem>
+#include <QList>
+#include <QAction>
+
+#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<const Fm::IconInfo> icon, QString title, Fm::FilePath path = Fm::FilePath{});
+    ~PlacesModelItem();
+
+    const std::shared_ptr<const Fm::FileInfo>& fileInfo() const {
+        return fileInfo_;
+    }
+    void setFileInfo(std::shared_ptr<const Fm::FileInfo> 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<const Fm::IconInfo>& icon() const {
+        return icon_;
+    }
+    void setIcon(std::shared_ptr<const Fm::IconInfo> 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<const Fm::FileInfo> fileInfo_;
+    std::shared_ptr<const Fm::IconInfo> 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<const Fm::BookmarkItem> bm_item);
+    const std::shared_ptr<const Fm::BookmarkItem>& bookmark() const {
+        return bookmarkItem_;
+    }
+private:
+    std::shared_ptr<const Fm::BookmarkItem> bookmarkItem_;
+};
+
+}
+
+#endif // FM_PLACESMODELITEM_H
diff --git a/src/placesview.cpp b/src/placesview.cpp
new file mode 100644 (file)
index 0000000..c99b835
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "placesview.h"
+#include "placesmodel.h"
+#include "placesmodelitem.h"
+#include "mountoperation.h"
+#include "fileoperation.h"
+#include <QMenu>
+#include <QContextMenuEvent>
+#include <QHeaderView>
+#include <QDebug>
+#include <QGuiApplication>
+#include "folderitemdelegate.h"
+
+namespace Fm {
+
+std::shared_ptr<PlacesProxyModel> PlacesView::proxyModel_;
+
+PlacesProxyModel::PlacesProxyModel(QObject* parent) :
+    QSortFilterProxyModel(parent),
+    showAll_(false),
+    hiddenItemsRestored_(false) {
+}
+
+PlacesProxyModel::~PlacesProxyModel() {
+}
+
+void PlacesProxyModel::restoreHiddenItems(const QSet<QString>& items) {
+    // hidden items should be restored only once
+    if(!hiddenItemsRestored_ && !items.isEmpty()) {
+        hidden_.clear();
+        QSet<QString>::const_iterator i = items.constBegin();
+        while (i != items.constEnd()) {
+            if(!(*i).isEmpty()) {
+                hidden_ << *i;
+            }
+            ++i;
+        }
+        hiddenItemsRestored_ = true;
+        invalidateFilter();
+    }
+}
+
+void PlacesProxyModel::setHidden(const QString& str, bool hide) {
+    if(hide) {
+        if(!str.isEmpty()) {
+            hidden_ << str;
+        }
+    }
+    else {
+        hidden_.remove(str);
+    }
+    invalidateFilter();
+}
+
+void PlacesProxyModel::showAll(bool show) {
+    showAll_ = show;
+    invalidateFilter();
+}
+
+bool PlacesProxyModel::filterAcceptsRow(int source_row, const QModelIndex& source_parent) const {
+    if(showAll_ || hidden_.isEmpty()) {
+        return true;
+    }
+    if(PlacesModel* srcModel = static_cast<PlacesModel*>(sourceModel())) {
+        QModelIndex index = srcModel->index(source_row, 0, source_parent);
+        if(PlacesModelItem* item = static_cast<PlacesModelItem*>(srcModel->itemFromIndex(index))) {
+            if(item->type() == PlacesModelItem::Places) {
+                if(auto path = item->path()) {
+                    if(hidden_.contains(path.toString().get())) {
+                        return false;
+                    }
+                }
+            }
+            else if(item->type() == PlacesModelItem::Volume) {
+                CStrPtr uuid{g_volume_get_uuid(static_cast<PlacesModelVolumeItem*>(item)->volume())};
+                if(uuid && hidden_.contains(uuid.get())) {
+                    return false;
+                }
+            }
+            // show a root items only if, at least, one of its children is shown
+            else if((source_row == 0 || source_row == 1) && !source_parent.isValid()) {
+                QModelIndex indx = index.child(0, 0);
+                while(PlacesModelItem* childItem = static_cast<PlacesModelItem*>(srcModel->itemFromIndex(indx))) {
+                    if(childItem->type() == PlacesModelItem::Places) {
+                        if(auto path = childItem->path()) {
+                            if(!hidden_.contains(path.toString().get())) {
+                                return true;
+                            }
+                        }
+                    }
+                    else if(childItem->type() == PlacesModelItem::Volume) {
+                        CStrPtr uuid{g_volume_get_uuid(static_cast<PlacesModelVolumeItem*>(childItem)->volume())};
+                        if(uuid == nullptr || !hidden_.contains(uuid.get())) {
+                            return true;
+                        }
+                    }
+                    else {
+                        return true;
+                    }
+                    indx = indx.sibling(indx.row() + 1, 0);
+                }
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+PlacesView::PlacesView(QWidget* parent):
+    QTreeView(parent) {
+    setRootIsDecorated(false);
+    setHeaderHidden(true);
+    setIndentation(12);
+
+    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<PlacesProxyModel>();
+    }
+    if(!proxyModel_->sourceModel()) { // all places-views may have been closed
+        proxyModel_->setSourceModel(model_.get());
+    }
+    setModel(proxyModel_.get());
+
+    // these 2 connections are needed to update filtering
+    connect(model_.get(), &QAbstractItemModel::rowsInserted, this, [this](const QModelIndex&, int, int) {
+        proxyModel_->setHidden(QString()); // just invalidates filter
+        expandAll();
+        // for some reason (a Qt bug?), spanning is reset
+        spanFirstColumn();
+    });
+    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();
+
+    spanFirstColumn();
+
+    // 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::spanFirstColumn() {
+    // 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
+    // NOTE: The first column of the devices children shouldn't be spanned
+    // because the second column contains eject buttons.
+    QModelIndex indx = proxyModel_->mapFromSource(model_->placesRoot->index());
+    if(indx.isValid()) {
+        for(int i = 0; i < indx.model()->rowCount(indx); ++i) {
+            setFirstColumnSpanned(i, indx, true);
+        }
+    }
+    indx = proxyModel_->mapFromSource(model_->bookmarksRoot->index());
+    if(indx.isValid()) {
+        for(int i = 0; i < indx.model()->rowCount(indx); ++i) {
+            setFirstColumnSpanned(i, indx, true);
+        }
+    }
+}
+
+void PlacesView::activateRow(int type, const QModelIndex& index) {
+    if(!index.parent().isValid()) { // ignore root items
+        return;
+    }
+    PlacesModelItem* item = static_cast<PlacesModelItem*>(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<PlacesModelVolumeItem*>(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<PlacesModelVolumeItem*>(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<PlacesModelMountItem*>(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<PlacesModelItem*>(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<PlacesModel::ItemAction*>(sender());
+    if(!action->index().isValid()) {
+        return;
+    }
+    PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(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<PlacesModel::ItemAction*>(sender());
+    if(!action->index().isValid()) {
+        return;
+    }
+    PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(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<PlacesModel::ItemAction*>(sender());
+    if(!action->index().isValid()) {
+        return;
+    }
+    PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(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<PlacesModelBookmarkItem*>(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<PlacesModel::ItemAction*>(sender());
+    if(!action->index().isValid()) {
+        return;
+    }
+    PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(action->index()));
+    if(item) {
+        Q_EMIT chdirRequested(1, item->path());
+    }
+}
+
+void PlacesView::onOpenNewWindow() {
+    PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
+    if(!action->index().isValid()) {
+        return;
+    }
+    PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(action->index()));
+    if(item) {
+        Q_EMIT chdirRequested(2, item->path());
+    }
+}
+
+void PlacesView::onRenameBookmark() {
+    PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
+    if(!action->index().isValid()) {
+        return;
+    }
+    PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(model_->itemFromIndex(action->index()));
+    setFocus();
+    setCurrentIndex(proxyModel_->mapFromSource(item->index()));
+    edit(proxyModel_->mapFromSource(item->index()));
+}
+
+void PlacesView::onMountVolume() {
+    PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
+    if(!action->index().isValid()) {
+        return;
+    }
+    PlacesModelVolumeItem* item = static_cast<PlacesModelVolumeItem*>(model_->itemFromIndex(action->index()));
+    MountOperation* op = new MountOperation(true, this);
+    op->mount(item->volume());
+    op->wait();
+}
+
+void PlacesView::onUnmountVolume() {
+    PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
+    if(!action->index().isValid()) {
+        return;
+    }
+    PlacesModelVolumeItem* item = static_cast<PlacesModelVolumeItem*>(model_->itemFromIndex(action->index()));
+    MountOperation* op = new MountOperation(true, this);
+    op->unmount(item->volume());
+    op->wait();
+}
+
+void PlacesView::onUnmountMount() {
+    PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
+    if(!action->index().isValid()) {
+        return;
+    }
+    PlacesModelMountItem* item = static_cast<PlacesModelMountItem*>(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<PlacesModel::ItemAction*>(sender());
+    if(!action->index().isValid()) {
+        return;
+    }
+    PlacesModelVolumeItem* item = static_cast<PlacesModelVolumeItem*>(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<PlacesModelItem*>(model_->itemFromIndex(proxyModel_->mapToSource(index)));
+
+        if(index.parent().isValid()
+           && item->type() != PlacesModelItem::Mount
+           && (item->type() != PlacesModelItem::Volume
+               || static_cast<PlacesModelVolumeItem*>(item)->isMounted())) {
+            action = new PlacesModel::ItemAction(item->index(), tr("Open in New Tab"), menu);
+            connect(action, &QAction::triggered, this, &PlacesView::onOpenNewTab);
+            menu->addAction(action);
+            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<QAction*> actions = menu->actions();
+                    if(!actions.isEmpty()) {
+                        menu->insertAction(actions.at(0), action);
+                        menu->insertSeparator(actions.at(0));
+                    }
+                    else { // impossible
+                        menu->addAction(action);
+                    }
+                }
+                // add a "Hide" action to the end
+                menu->addSeparator();
+                action = new PlacesModel::ItemAction(item->index(), tr("Hide"), menu);
+                QString pathStr(path_str.get());
+                action->setCheckable(true);
+                if(proxyModel_->isShowingAll()) {
+                    action->setChecked(proxyModel_->isHidden(pathStr));
+                }
+                connect(action, &QAction::triggered, [this, pathStr](bool checked) {
+                    proxyModel_->setHidden(pathStr, checked);
+                    Q_EMIT hiddenItemSet(pathStr, checked);
+                });
+                menu->addAction(action);
+            }
+            break;
+        }
+        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<PlacesModelVolumeItem*>(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<PlacesModelVolumeItem*>(item)->volume())};
+            if(uuid) {
+                QString str = uuid.get();
+                menu->addSeparator();
+                action = new PlacesModel::ItemAction(item->index(), tr("Hide"), menu);
+                action->setCheckable(true);
+                if(proxyModel_->isShowingAll()) {
+                    action->setChecked(proxyModel_->isHidden(str));
+                }
+                connect(action, &QAction::triggered, [this, str](bool checked) {
+                    proxyModel_->setHidden(str, checked);
+                    Q_EMIT hiddenItemSet(str, checked);
+                });
+                menu->addAction(action);
+            }
+            break;
+        }
+        case PlacesModelItem::Mount: {
+            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<QString>& items) {
+    proxyModel_->restoreHiddenItems(items);
+}
+
+void PlacesView::showAll(bool show) {
+    proxyModel_->showAll(show);
+    if(show) {
+        expandAll();
+        // for some reason (a Qt bug?), spanning is reset
+        spanFirstColumn();
+    }
+}
+
+} // namespace Fm
diff --git a/src/placesview.h b/src/placesview.h
new file mode 100644 (file)
index 0000000..9dba4ed
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2012 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_PLACESVIEW_H
+#define FM_PLACESVIEW_H
+
+#include "libfmqtglobals.h"
+#include <QTreeView>
+#include <QSortFilterProxyModel>
+
+#include <memory>
+#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<QString>& 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<QString> 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<QString>& 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);
+    void spanFirstColumn();
+
+private:
+    std::shared_ptr<PlacesModel> model_;
+    Fm::FilePath currentPath_;
+
+    static std::shared_ptr<PlacesProxyModel> proxyModel_; // used to hide items in all views
+};
+
+}
+
+#endif // FM_PLACESVIEW_H
diff --git a/src/proxyfoldermodel.cpp b/src/proxyfoldermodel.cpp
new file mode 100644 (file)
index 0000000..f66b3a8
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "proxyfoldermodel.h"
+#include "foldermodel.h"
+#include <QCollator>
+
+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<FolderModel*>(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<FolderModel*>(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<FolderModel*>(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<FolderModel*>(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<FolderModel*>(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<FolderModel*>(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<const Fm::FileInfo> ProxyFolderModel::fileInfoFromIndex(const QModelIndex& index) const {
+    if(index.isValid()) {
+        FolderModel* srcModel = static_cast<FolderModel*>(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<const FileInfo> ProxyFolderModel::fileInfoFromPath(const FilePath &path) const {
+    return fileInfoFromIndex(indexFromPath(path));
+}
+
+void ProxyFolderModel::setCutFiles(const QItemSelection& selection) {
+    FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
+    if(srcModel) {
+        srcModel->setCutFiles(mapSelectionToSource(selection));
+    }
+}
+
+void ProxyFolderModel::setShowThumbnails(bool show) {
+    if(show != showThumbnails_) {
+        showThumbnails_ = show;
+        FolderModel* srcModel = static_cast<FolderModel*>(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<FolderModel*>(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<FolderModel*>(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<FolderModel*>(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<FolderModel*>(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
new file mode 100644 (file)
index 0000000..b8ff4dc
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_PROXYFOLDERMODEL_H
+#define FM_PROXYFOLDERMODEL_H
+
+#include "libfmqtglobals.h"
+#include <QSortFilterProxyModel>
+#include <QList>
+#include <QCollator>
+
+#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<const Fm::FileInfo>& 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<const Fm::FileInfo> fileInfoFromIndex(const QModelIndex& index) const;
+
+    std::shared_ptr<const Fm::FileInfo> 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<ProxyFolderModelFilter*> filters_;
+};
+
+}
+
+#endif // FM_PROXYFOLDERMODEL_H
diff --git a/src/rename-dialog.ui b/src/rename-dialog.ui
new file mode 100644 (file)
index 0000000..2b0c123
--- /dev/null
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>RenameDialog</class>
+ <widget class="QDialog" name="RenameDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>398</width>
+    <height>220</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Confirm to replace files</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>false</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>6</number>
+   </property>
+   <property name="margin">
+    <number>10</number>
+   </property>
+   <item>
+    <widget class="QLabel" name="label">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="text">
+      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <property name="horizontalSpacing">
+      <number>12</number>
+     </property>
+     <property name="verticalSpacing">
+      <number>6</number>
+     </property>
+     <item row="0" column="0">
+      <widget class="QLabel" name="destIcon">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>dest</string>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="0" colspan="2">
+      <widget class="QLabel" name="label_3">
+       <property name="text">
+        <string>with the following file?</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QLabel" name="srcInfo">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>src file info</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1">
+      <widget class="QLabel" name="destInfo">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>dest file info</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="0">
+      <widget class="QLabel" name="srcIcon">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>src</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <property name="spacing">
+      <number>12</number>
+     </property>
+     <item>
+      <widget class="QLabel" name="label_6">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>&amp;File name:</string>
+       </property>
+       <property name="buddy">
+        <cstring>fileName</cstring>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="fileName"/>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="applyToAll">
+     <property name="text">
+      <string>Apply this option to all existing files</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeType">
+      <enum>QSizePolicy::Expanding</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ignore|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>RenameDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>RenameDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/src/renamedialog.cpp b/src/renamedialog.cpp
new file mode 100644 (file)
index 0000000..cf70670
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "renamedialog.h"
+#include "ui_rename-dialog.h"
+#include <QStringBuilder>
+#include <QPushButton>
+#include <QDateTime>
+
+#include "core/iconinfo.h"
+#include "utilities.h"
+
+#include "core/legacy/fm-config.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<QPushButton*>(ui->buttonBox->button(QDialogButtonBox::Ok));
+    overwriteButton->setEnabled(!hasNewName);
+    overwriteButton->setDefault(!hasNewName);
+}
+
+
+} // namespace Fm
diff --git a/src/renamedialog.h b/src/renamedialog.h
new file mode 100644 (file)
index 0000000..c068ac0
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_RENAMEDIALOG_H
+#define FM_RENAMEDIALOG_H
+
+#include "libfmqtglobals.h"
+#include <QDialog>
+
+#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
new file mode 100644 (file)
index 0000000..7325d48
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#include "sidepane.h"
+#include <QComboBox>
+#include <QVBoxLayout>
+#include <QHeaderView>
+#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<void (QComboBox::*)(int)>(&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<PlacesView*>(view_)->setIconSize(size);
+        /* Falls through. */
+    case ModeDirTree:
+        static_cast<QTreeView*>(view_)->setIconSize(size);
+        break;
+    default:
+        ;
+    }
+}
+
+void SidePane::setCurrentPath(Fm::FilePath path) {
+    Q_ASSERT(path);
+    currentPath_ = std::move(path);
+    switch(mode_) {
+    case ModePlaces:
+        static_cast<PlacesView*>(view_)->setCurrentPath(currentPath_);
+        break;
+    case ModeDirTree:
+        static_cast<DirTreeView*>(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<PlacesView*>(view_);
+        return true;
+    case ModeDirTree:
+        // static_cast<PlacesView*>(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<DirTreeView*>(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);
+
+        // visually merge it with its surroundings
+        placesView->setFrameShape(QFrame::NoFrame);
+        QPalette p = placesView->palette();
+        p.setColor(QPalette::Base, QColor(Qt::transparent));
+        p.setColor(QPalette::Text, p.color(QPalette::WindowText));
+        placesView->setPalette(p);
+        placesView->viewport()->setAutoFillBackground(false);
+
+        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<DirTreeView*>(view_);
+        DirTreeModel* model = static_cast<DirTreeModel*>(dirTreeView->model());
+        if(model) {
+            model->setShowHidden(showHidden_);
+        }
+    }
+}
+
+void SidePane::restoreHiddenPlaces(const QSet<QString>& items) {
+    if(mode_ == ModePlaces) {
+        static_cast<PlacesView*>(view_)->restoreHiddenItems(items);
+    }
+    else {
+        restorableHiddenPlaces_.unite(items);
+    }
+}
+
+} // namespace Fm
diff --git a/src/sidepane.h b/src/sidepane.h
new file mode 100644 (file)
index 0000000..9ae7935
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+
+#ifndef FM_SIDEPANE_H
+#define FM_SIDEPANE_H
+
+#include "libfmqtglobals.h"
+#include <QWidget>
+
+#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<QString>& 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<QString> restorableHiddenPlaces_;
+};
+
+}
+
+#endif // FM_SIDEPANE_H
diff --git a/src/tests/test-filedialog.cpp b/src/tests/test-filedialog.cpp
new file mode 100644 (file)
index 0000000..ccd1809
--- /dev/null
@@ -0,0 +1,48 @@
+#include <QApplication>
+#include <QMainWindow>
+#include <QToolBar>
+#include <QDebug>
+#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
new file mode 100644 (file)
index 0000000..45af775
--- /dev/null
@@ -0,0 +1,44 @@
+#include <QApplication>
+#include <QDebug>
+#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<Fm::FileInfoPair>& 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
new file mode 100644 (file)
index 0000000..427f1e5
--- /dev/null
@@ -0,0 +1,48 @@
+#include <QApplication>
+#include <QMainWindow>
+#include <QToolBar>
+#include <QDebug>
+#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
new file mode 100644 (file)
index 0000000..ddfe213
--- /dev/null
@@ -0,0 +1,20 @@
+#include <QApplication>
+#include <QMainWindow>
+#include <QToolBar>
+#include <QDir>
+#include <QDebug>
+#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
new file mode 100644 (file)
index 0000000..7b6438a
--- /dev/null
@@ -0,0 +1,24 @@
+#include <QApplication>
+#include <QDir>
+#include <QDebug>
+#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/translations/CMakeLists.txt b/src/translations/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fea1753
--- /dev/null
@@ -0,0 +1,3 @@
+project(libfm-qt)
+
+build_component("." "${CMAKE_INSTALL_FULL_DATADIR}/libfm-qt/translations")
diff --git a/src/translations/libfm-qt.ts b/src/translations/libfm-qt.ts
new file mode 100644 (file)
index 0000000..f079181
--- /dev/null
@@ -0,0 +1,1547 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_ar.ts b/src/translations/libfm-qt_ar.ts
new file mode 100644 (file)
index 0000000..b93b84c
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ar">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>اختر تطبيقا</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>التطبيقات المثبّتة</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>أمر مخصّص</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>سطر الأوامر لتنفيذه:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>اسم التطبيق:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;يمكن استخدام هذه العلامات الخاصّة في سطر الأوامر:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: يشير إلى اسم ملف واحد&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: يشير إلى أسماء عدّة ملفات&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: يشير إلى مسار واحد للملف&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: يشير إلى عدّة مسارات&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>أبقِ نافذة الطرفية مفتوحة بعد تنفيذ الأمر</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>نفّذ في محاكي طرفية</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>اضبط التطبيق المنتقى ليكون الإجراء المبدئي لنوع الملفات هذا</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>حرّر العلامات</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>الاسم</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>المكان</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>أ&amp;ضِف عنصرا</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>أ&amp;زِل العنصر</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>استخدم السحب والإفلات لإعادة ترتيب العناصر</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>نفّذ الملف</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>ا&amp;فتح</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>&amp;نفّذ</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>نفّذ في ال&amp;طرفية</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>ألغِ</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>المكان:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>اسم الملف:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>نوع الملف:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>المقصد:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>يعالج:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>يحضّر…</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>التقدّم</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>الوقت المتبقي:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>الملفات المعالجة:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>خصائص الملف</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>عام</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>المكان:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>نوع الملف:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>نوع MIME:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>حجم الملف:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>الحجم على القرص:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>آخر تعديل:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>هدف الرابط:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>افتح في:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>آخر نفاذ:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation>يحتوي على:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>استخدام الجهاز:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>التصاريح</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>الملكية</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>المجموعة:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>المالك:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>التحكم بالنفاذ</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>الغير:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>اجعل الملف تنفيذيا</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>القراءة</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>الكتابة</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>التنفيذ</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>ملتصق</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>ضبط معرّف المستخدم</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>ضبط معرّف المجموعة</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>الوضع المتقدم</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>خصّص</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>اختر تطبيقا لفتح ملفات ”%1“</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>مجلد</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>ملف فارغ</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>الدليل المحدد ”%1“ ليس صالحا</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>يحمّل…</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;لا مجلدات فرعية&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>افتح في ل&amp;سان جديد</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>افتح في &amp;نافذة جديدة</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>افتح في ال&amp;طرفية</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>انسخ هنا</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>انقل هنا</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>أنشِئ رابطا رمزيا هنا</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>ألغِ</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>علامة جديدة</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>يبدو الملف ”%1“ مدخلة سطح مكتب.
+ما الذي تريد فعله معه؟</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>يبدو الملف النصي ”%1“ سكربتا تنفيذيا.
+ما الذي تريد فعله معه؟</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>الملف ”%1“ تنفيذي. أتريد تنفيذه؟</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>عُد</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Left</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>تقدّم</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Right</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>أعِد التحميل</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>أنشِئ مجلدا</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>منظور أيقوناتي</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>منظور مصغّراتي</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>منظور متضام</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>منظور قائمة مفصّلة</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>خطأ</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>رجاءً اختر ملفا</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>‏⁨%1⁩ موجود بالفعل.
+أتريد استبداله؟</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>المسار ”%1“ غير موجود</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>‏”⁨%1⁩“ ليس دليلا</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>‏”⁨%1⁩“ ليس ملفا</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>ا&amp;فتح</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>ا&amp;حفظ</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>كل الملفات (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>فتح الملف</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>حفظ الملف</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>افتح</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>قصّ</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>انسخ</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>ألصِق</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>ا&amp;نقل إلى المهملات</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>الوثوق بالملفات التنفيذية المحددة</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>الوثوق بهذا الملف التنفيذي المحدد</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>الخرج</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>ا&amp;حذف</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>غيّر الاسم</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>افتح في…</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>تطبيق آخر</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>أنشِئ &amp;جديدا</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>ا&amp;ستعد</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>استخرج إلى…</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>استخرج هنا</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>اضغط</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>خصائص</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>خطأ</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>تعذر نقل بعض الملفات إلى المهملات لأن أنظمة الملفات التحتية لا تدعم ذلك.
+أتريد حذفها بدل نقلها؟</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>تأكيد</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>أتريد حذف الملفات المحددة؟</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>أتريد نقل الملفات المحددة إلى سلة المهملات؟</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>نقل الملفات</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>ينقل الملفات الآتية إلى المجلد المقصد:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>نسخ الملفات</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>ينسخ الملفات الآتية إلى المجلد المقصد:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>رمي الملفات</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>ينقل الملفات الآتية إلى سلة المهملات:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>حذف الملفات</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>يحذف الملفات الآتية:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>إنشاء الروابط الرمزية</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>ينشئ روابط رمزية للملفات الآتية:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>تغيير الصفات</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>يغيّر صفات الملفات الآتية:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>استعادة الملفات المرمية</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>يستعيد الملفات الآتية من سلة المهملات:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>خطأ</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>عرض محتوى المجلد</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>عرض محتوى المجلد وتعديله</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>القراءة</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>القراءة والكتابة</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>ممنوع</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>ملفات أنواعها مختلفة</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>عدد من الملفات</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>%p% مستخدم</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>%1% متاح من %2</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>لا ملف</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation>ملف واحد</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation>%1 ملفات</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>اختر أيقونة</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>الصور (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>تطبيق التعديلات</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>أتريد تطبيق هذه التعديلات تكراريا لكل الملفات والمجلدات الفرعية؟</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>خطأ</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>عليك إضافة دليل واحد على الأقل للبحث فيه.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>اختر مجلدا</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>لا يمكن إنشاء رابط في نظام ملفات غير أصيل</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>أنشِئ &amp;جديدا</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>أل&amp;صِق</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>حدّد ال&amp;كل</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>اعكس التحديد</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>الفرز</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>اعرض المخفي</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>&amp;خصائص المجلد</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>الخرج</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>باسم الملف</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>بوقت التعديل</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>بحجم الملف</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>بنوع الملف</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>بمالك الملف</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>تصاعديا</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>تنازليا</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>المجلدات أولا</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>حساس للحالة</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>الاسم</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>النوع</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>الحجم</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>التعديل</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>المالك</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>المجموعة</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>ثخين</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>مائل</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>اتّ&amp;صل</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;حرّر المسار</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>ا&amp;نسخ المسار</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>الأماكن</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>سطح المكتب</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>المهملات</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>الحاسوب</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>التطبيقات</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>الشبكة</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>الأجهزة</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>العلامات</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>أفرِغ المهملات</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>افتح في لسان جديد</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>افتح في نافذة جديدة</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>أخفِ</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>انقل العلامة لأعلى</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>انقل العلامة لأسفل</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>غيّر اسم العلامة</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>أزِل العلامة</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>ألغِ الضمّ</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>ضُمّ</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>أخرِج</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>اعرض كل المدخلات</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>النوع: %1
+الحجم: %2
+التعديل: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>النوع: %1
+التعديل: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>ا&amp;كتب فوقه</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;غيّر اسمه</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>الأماكن</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>شجرة الأدلة</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>تعذر إلغاء رمي الملف ”%s“: المسار الأصلي مجهول</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>ضُمّ</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>اتّصل كم&amp;جهول</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>اتّصل كالمست&amp;خدم:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>ا&amp;سم المستخدم:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;كلمة السر:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>الن&amp;طاق:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>انسَ كلمة السر مبا&amp;شرة</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>تذكّر كلمة السر حتى أ&amp;خرج</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>تذكّر للأب&amp;د</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>خطأ</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>تغيير اسم الملف</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>رجاءً أدخِل الاسم الجديد:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>إنشاء مجلد</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>رجاءً أدخِل اسم الملف الجديد:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>ملف نصي جديد</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>رجاءً أدخِل اسم المجلد الجديد:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>مجلد جديد</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>رجاءً أدخِل اسما لِ‍ ”%1“ الجديد:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>إنشاء ملف</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>خطأ في الأيقونة المخصصة</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>المسار ليس مضموما.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>ملف مدخلة سطح المكتب غير صالح: ”%1“</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>لم يُضبط أي تطبيق مبدئي ليُطلق ”%1“</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>تعذر ضبط دليل العمل ليكون ”%1“: ‏”%2“</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation>معرف: </translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>تأكيد استبدال الملفات</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;ثمة ملف بنفس الاسم في هذا المكان بالفعل.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;أتريد استبدال الملف الموجود؟&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>المقصد</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>بالملف الآتي؟</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>معلومات الملف المصدر</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>معلومات الملف المقصد</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>المصدر</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>اسم المل&amp;ف:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>طبّق هذا الخيار على كل الملفات الموجودة</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>بحث عن ملفات</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>الاسم/المكان</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>أنماط لأسماء الملفات:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>غير حسّاس للحالة</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>استخدم التعابير النمطية</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>أماكن البحث:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>أ&amp;ضِف</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>أ&amp;زِل</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>ابحث في المجلدات الفرعية</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>ابحث عن الملفات المخفية</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>نوع الملف</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>ابحث عن الملفات بالأنواع الآتية فقط:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>الملفات النصية</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>ملفات الصور</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>ملفات الصوت</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>ملفات الڤديو</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>المستندات</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>المجلدات</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>المحتوى</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>في داخل الملف:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>&amp;غير حسّاس للحالة</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>ا&amp;ستخدم التعابير النمطية</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>الخصائص</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>حجم الملف:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>أكبر من:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>بايت</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>ك.بايت</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>م.بايت</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>غ.بايت</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>أصغر من:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>آخر وقت تعديل:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>كان قبل:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>كان بعد:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_ca.ts b/src/translations/libfm-qt_ca.ts
new file mode 100644 (file)
index 0000000..8415977
--- /dev/null
@@ -0,0 +1,1561 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ca">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Trieu una aplicació</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Aplicacions instal·lades</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Ordre personalitzada</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Línia d&apos;ordres a executar:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Nom de l&apos;aplicació:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;En la línia d&apos;ordres es poden utilitzar aquests codis especials:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Representa un sol nom de fitxer&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Representa múltiples noms de fitxers&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Representa una sola URI del fitxer&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Representa múltiples URI&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Mantén oberta la finestra del terminal després de l&apos;execució de l&apos;ordre</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Executa a l&apos;emulador del terminal</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Estableix l&apos;aplicació seleccionada com a acció predeterminada d&apos;aquest tipus de fitxer</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Edició dels marcadors</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Nom</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Ubicació</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Afegeix un ítem</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>Sup&amp;rimeix l&apos;ítem</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Utilitzeu arrossega i deixa anar per a reordenar els ítems</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Executa el fitxer</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Obre</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>E&amp;xecuta</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Executa al &amp;terminal</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Cancel·la</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Ubicació:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Nom de fitxer:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Tipus de fitxer:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Destinació:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Processament:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>S&apos;està preparant...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Progrés</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Temps restant:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Fitxers processats:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Propietats del fitxer</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>General</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Ubicació:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Tipus de fitxer:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Tipus MIME:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Mida del fitxer:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Mida al disc:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Última modificació:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Objectiu de l&apos;enllaç:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Obre amb:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Últim accés:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation>Conté:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>Ús del dispositiu:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Permisos</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Propietat</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Grup:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Propietari:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Control d&apos;accés</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Altres:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Fes que el fitxer sigui executable</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Lectura</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Escriptura</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Execució</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Fix</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>SetUID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>SetGID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Mode avançat</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Personalitza</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Seleccioneu una aplicació per obrir els fitxers &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Carpeta</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Fitxer en blanc</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>El directori especificat «%1» no és vàlid</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>S&apos;està carregant...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Sense subcarpetes&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Obre en una pes&amp;tanya nova</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Obre en una &amp;finestra nova</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Obre al termina&amp;l</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Copia aquí</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Mou aquí</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Copia aquí l&apos;enllaç simbòlic</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Cancel·la</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Nou marcador</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>El fitxer «%1» sembla que sigui una entrada d&apos;escriptori.
+Què en voleu fer?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Aquest fitxer de text «%1» pel que sembla és un script executable.
+Què voleu fer amb ell?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Aquest fitxer «%1» és executable. Voleu executar-lo?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Vés enrere</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Esquerra</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Vés endavant</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Dreta</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Torna a carregar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Crea una carpeta</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Vista d&apos;icones</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Vista de miniatures</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Vista compacta</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Vista de llista detallada</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Seleccioneu un fitxer</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 ja existeix.
+Voleu substituir-ho?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>No existeix el camí «%1»</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>«%1» no és un directori</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>«%1» no és un fitxer</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Obre</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>De&amp;sa</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Tots els fitxers (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Obre un fitxer</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Desa el fitxer</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Obre</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Obre amb...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Altres aplicacions</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Crea &amp;nou</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Restaura</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Retalla</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Copia</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Enganxa</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>&amp;Mou a la paperera</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Reanomena</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Extreu a...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Extreu aquí</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Comprimeix</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Propietats</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Confia amb els executables seleccionats</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Confia amb aquest executable</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Sortida</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Suprimeix</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Alguns fitxers no es poden moure a la paperera perquè els sistemes de fitxers subjacents no són compatibles amb aquesta operació.
+En lloc seu, voleu suprimir-los?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Confirma</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Voleu suprimir els fitxers seleccionats?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Voleu moure els fitxers seleccionats a la paperera?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Mou els fitxers</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>S&apos;estan movent els següents fitxers a la carpeta de destinació:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Copia els fitxers</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>S&apos;estan copiant els següents fitxers a la carpeta de destinació:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Mou els fitxers a la paperera</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>S&apos;estan movent els següents fitxers a la paperera:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Suprimeix els fitxers</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>S&apos;estan suprimint els següents fitxers:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Crea enllaços simbòlics</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>S&apos;estan creant els enllaços simbòlics per als següents fitxers:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Canvia els atributs</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>S&apos;estan canviant els atributs per als següents fitxers:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Restaura els fitxers de la paperera</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>S&apos;estan restaurant els següents fitxers de la paperera:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Visualització del contingut de la carpeta</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Visualització i modificació del contingut de la carpeta</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Lectura</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Lectura i escriptura</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Prohibit</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Fitxers de diferents tipus</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Múltiples fitxers</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>%p% utilitzat</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>%1 lliure de %2</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>cap fitxer</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation>un fitxer</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation>%1 fitxers</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Seleccioneu una icona</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Imatges (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Aplica els canvis</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Voleu aplicar recursivament aquests canvis a tots els fitxers i subcarpetes?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Com a mínim heu de seleccionar una carpeta per cercar.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Seleccioneu una carpeta</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>No es pot crear un enllaç en un sistema de fitxers no natiu</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Crea &amp;nou</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>En&amp;ganxa</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Seleccion&amp;a-ho tot</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Inverteix la selecció</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Ordenació</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Mostra els ocults</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Pr&amp;opietats de la carpeta</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Sortida</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Pel nom del fitxer</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Per la data de modificació</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Per la mida del fitxer</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Pel tipus de fitxer</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Pel propietari del fitxer</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Ascendent</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Descendent</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Primer les carpetes</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Distinció entre majúscules i minúscules</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Nom</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Tipus</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Mida</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Modificat</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Propietari</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Grup</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Negreta</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Cursiva</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Connecta</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Edita el camí</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Copia el camí</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Llocs</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Escriptori</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Ordinador</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Aplicacions</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Xarxa</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Dispositius</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Marcadors</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Paperera</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Obre en una pestanya nova</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Obre en una finestra nova</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Buida la paperera</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Oculta</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Mou amunt el marcador</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Mou avall el marcador</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Reanomena el marcador</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Suprimeix el marcador</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Desmunta</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Munta</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Expulsa</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Mostra totes les entrades</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Tipus: %1
+Mida: %2
+Modificat: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Tipus: %1
+Modificat: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>S&amp;obreescriu</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Reanomena</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Llocs</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Arbre de directoris</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>No es pot treure de la paperera el fitxer «%s»: camí original no conegut</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Munta</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Connecta &amp;anònimament</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Connecta com a l&apos;&amp;usuari:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>Nom d&apos;&amp;usuari:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Contrasenya:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translatorcomment>&amp;Domini:</translatorcomment>
+        <translation>&amp;Domini:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Oblida &amp;immediatament la contrasenya</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Recorda la contrasenya fins que no es tanqui &amp;la sessió</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Recorda-la sem&amp;pre</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Reanomena el fitxer</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Introduïu un nom nou:</translation>
+    </message>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Crea una carpeta</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Crea un fitxer</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Introduïu el nom del fitxer nou:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Fitxer de text nou</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Introduïu el nom de la carpeta nova:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Carpeta nova</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Introduïu el nom per al nou %1:</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Error de la icona personalitzada</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>El camí no està muntat.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Fitxer d&apos;entrada d&apos;escriptori no vàlid: «%1»</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>No s&apos;ha establert cap aplicació predeterminada per obrir «%1»</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>No es pot establir el directori de treball a «%1»: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation>Identificador: </translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Confirmació per substituir els fitxers</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Ja hi ha un fitxer amb el mateix nom en aquesta ubicació.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Voleu substituir el fitxer existent?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>dest</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>amb el següent fitxer?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>info del fitxer ori</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>info del fitxer dest</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>ori</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>Nom del &amp;fitxer:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Aplica aquesta opció a tots els fitxers existents</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Cerca fitxers</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Nom/Ubicació</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Patrons dels noms dels fitxers:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Distinció entre majúscules i minúscules</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Utilitza l&apos;expressió regular</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Llocs a cercar:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Afegeix</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>Sup&amp;rimeix</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Cerca als subdirectoris</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Cerca els fitxers ocults</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Tipus de fitxer</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Cerca únicament els següents tipus de fitxers:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Fitxers de text</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Fitxers d&apos;imatges</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Fitxers d&apos;àudio</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Fitxers de vídeo</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Documents</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Carpetes</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Contingut</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>El fitxer conté:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Distinció entre ma&amp;júscules i minúscules</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>&amp;Utilitza l&apos;expressió regular</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Propietats</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Mida del fitxer:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Més gran que:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Bytes</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Més petit que:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Data de l&apos;última modificació:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Abans de:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Després de:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_cs.ts b/src/translations/libfm-qt_cs.ts
new file mode 100644 (file)
index 0000000..4a661d5
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="cs">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Zvolte aplikaci</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Nainstalované aplikace</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Uživatelem určený příkaz</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Příkaz k vykonání:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Název aplikace:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;V příkazovém řádku je možné využít tato zástupná vyjádření:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Představuje jeden soubor&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Představuje vícero souborů&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Představuje URI identifikátor jednoho souboru&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Představuje vícero URI identifikátorů&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Po dokončení příkazu nezavírat okno terminálu</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Spustit v emulátoru terminálu</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Použít zvolenou aplikaci jako výchozí akci pro soubory tohoto typu</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Upravit záložky</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Název</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Umístění</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>Přid&amp;at položku</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>Odeb&amp;rat položku</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Pořadí položek upravíte přetažením</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Spustit soubor</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Otevřít</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>&amp;Spustit</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Spustit v &amp;terminálu</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Storno</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Umístění:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Název souboru:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Typ souboru:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Cílové umístění:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Zpracování:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Připravuje se…</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Ukazatel průběhu</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Bude trvat ještě:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Zpracovaných souborů:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Vlastnosti souboru</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Obecné</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Umístění:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Typ souboru:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>MIME typ:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Velikost souboru:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Zabrané místo na disku:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Naposledy změněno:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Cíl odkazu:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Otevřít pomocí:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Naposledy přistupováno:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation>Obsahuje:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>Využití zařízení:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Oprávnění</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Vlastnictví</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Skupina:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Vlastník:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Řízení přístupu</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Ostatní:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Označit soubor jako spustitelný</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Čtení</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Zápis</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Spuštění</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Mazat/přejmenovávat pouze vlastník (sticky)</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>Spouštět s právy vlastníka souboru (SetUID)</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>Přebírat skupinu u nových podsložek a souborů (SetGID)</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Pokročilý režim</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Přizpůsobit</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Vyberte aplikace, ve které otevírat soubory „%1“</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Složka</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Prázdný soubor</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>Zadaná složka „%1“ není platná</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Načítání…</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Žádné podsložky&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Otevřít v nové k&amp;artě</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>&amp;Otevřít v novém okně</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Otevřít v terminá&amp;lu</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Zkopírovat sem</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Přesunout sem</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Vytvořit zde symbolický odkaz</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Storno</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Nová záložka</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Zdá se, že soubor „%1“ je spouštěč.
+Co s ním chcete dělat?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Zdá se, že textový soubor „%1“ je spustitelný skript.
+Co s ním chcete dělat?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Tento soubor „%1“ je spustitelný. Chcete ho spustit?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Zpět</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt + šipka doleva</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Vpřed</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt + šipka doprava</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Načíst znovu</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Vytvořit složku</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Zobrazení s ikonami</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Zobrazení s náhledy</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Kompaktní zobrazení</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Podrobný seznam</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Chyba</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Vyberte soubor</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 už existuje.
+Chcete ho nahradit?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Umístění „%“ neexistuje</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>„%1“ není složka</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>„%1“ není soubor</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Otevřít</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;Uložit</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Všechny soubory (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Otevřít soubor</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Uložit soubor</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Otevřít</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Vytvořit &amp;nový</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>O&amp;bnovit</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Vyjmout</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Kopírovat</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Vložit</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>Přesunout do &amp;koše</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Důvěřovat označeným spustitelným</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Důvěřovat tomuto spustitelnému souboru</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Výstup</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Smazat</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Přejmenovat</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Otevřít s…</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Ostatní aplikace</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Rozbalit do…</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Rozbalit sem</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Komprimovat</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Vlastnosti</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Chyba</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Některé soubory nemohou být přesunuty do koše – souborové systémy, na kterých leží, tuto operaci nepodporují.
+Chcete je namísto toho přímo odstranit?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Potvrdit</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Chcete vybrané soubory smazat?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Chcete vybrané soubory přesunout do koše?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Přesunout soubory</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Přesouvání následujících souborů do cílové složky:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Kopírovat soubory</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Kopírování následujících souborů do cílové složky:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Soubory v Koši</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Přesouvání následujících souborů do koše:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Smazat soubory</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Mazání následujících souborů:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Vytvořit symbolické odkazy</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Vytváření symbolických odkazů pro následující soubory:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Změnit atributy</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Měnění atributů u následujících souborů:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Obnovit soubory z koše</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Obnovování následujících souborů z koše:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Chyba</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Zobrazit obsah složky</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Zobrazit a změnit obsah složky</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Čtení</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Čtení a zápis</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Odepřen</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Soubory různých typů</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Vícero souborů</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>%p% použito</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>volných %1 z %2</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>žádný soubor</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation>jeden soubor</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation>%1 souborů</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Vybrat ikonu</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Obrázky (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Použít změny</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Chcete tyto změny použít také na všechny obsažené soubory a podsložky?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Chyba</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Přidejte alespoň jednu složku, ve které vyhledávat.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Vybrat složku</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Nelze vytvořit odkaz na souborovém systému, který to nepodporuje</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Vytvořit &amp;nový</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Vložit</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Vybr&amp;at vše</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Prohodit výběr</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Řazení</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Zobrazit skryté</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Vlastnosti sl&amp;ožky</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Výstup</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Podle názvu</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Podle okamžiku změny</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Podle velikosti</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Podle typu</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Podle vlastníka</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Vzestupně</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Sestupně</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Složky jako první</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Rozlišovat malá/VELKÁ písmena</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Název</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Typ</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Velikost</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Změněno</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Vlastník</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Skupina</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Tučné</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Kurzíva</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Připojit</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Upravit popis umístění</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Zkopírovat popis umístění</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Místa</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Plocha</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Koš</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Počítač</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Aplikace</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Síť</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Zařízení</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Záložky</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Vysypat koš</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Otevřít v novém panelu</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Otevřít v novém okně</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Skrýt</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Posunout záložku nahoru</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Posunout záložku dolů</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Přejmenovat záložku</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Odstranit záložku</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Odpojit</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Připojit</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Vysunout</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Zobrazit všechny položky</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Typ: %1
+Velikost: %2
+Změněno: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Typ: %1
+Změněno: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>&amp;Přepsat</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>Pře&amp;jmenovat</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Místa</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Strom složek</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Nelze obnovit soubor „%s“: původní umístění není známo</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Připojit</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Připojit &amp;anonymně</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Připojit jako &amp;uživatel:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>&amp;Uživatelské jméno:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Heslo:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Doména:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Heslo okamž&amp;itě zapomenout</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Zapamatovat si heslo do odh&amp;lášení</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Zapamatovat si heslo &amp;trvale</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Chyba</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Přejmenovat soubor</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Zadejte nový název:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Vytvořit složku</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Zadejte název pro nový soubor:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Nový textový soubor</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Zadejte název pro novou složku:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Nová složka</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Zadejte název pro nový %1:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Vytvořit soubor</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Chyba uživatelem určené ikony</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Umístění není připojené (mount).</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Neplatný soubor spouštěče: „%1“</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Pro spouštění „%1“ není nastavená žádná výchozí aplikace</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Nedaří se nastavit pracovní složku na „%1“: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation>Identifikátor: </translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Potvrdit nahrazení souborů</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;V tomto umístění se už nachází soubor se stejným názvem.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Chcete existující soubor nahradit?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>Cíl</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>následujícím souborem?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>Informace o zdrojovém souboru</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>Informace o cílovém souboru</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>Zdroj</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>Název &amp;souboru:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Použít tuto volbu pro všechny soubory</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Hledat soubory</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Název/umístění</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Název souboru obsahuje:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Nerozlišovat malá/VELKÁ písmena</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Použít regulární výrazy</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Místa k prohledání:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>Přid&amp;at</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>Odst&amp;ranit</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Hledat v podsložkách</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Hledat skryté soubory</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Typ souboru</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Hledat pouze soubory těchto typů:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Textové soubory</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Obrázky</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Zvuky</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Videa</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Dokumenty</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Složky</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Obsah</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Soubor obsahuje:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Nerozlišovat malá/&amp;VELKÁ písmena</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>Po&amp;užít regulární výrazy</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Vlastnosti</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Velikost souboru:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Větší než:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Bajtů</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Menší než:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Okamžik poslední změny:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Dříve než:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Později než:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_cy.ts b/src/translations/libfm-qt_cy.ts
new file mode 100644 (file)
index 0000000..dbfd5b9
--- /dev/null
@@ -0,0 +1,1547 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="cy">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation></translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_da.ts b/src/translations/libfm-qt_da.ts
new file mode 100644 (file)
index 0000000..73fbc8a
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="da">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Vælg et program</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Installerede programmer</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Valgfri kommando</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Udfør følgende kommando:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Programnavn:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Disse specielle koder kan bruges i kommandoen:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Repræsenterer ét filnavn&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Repræsenterer flere filnavne&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Repræsenterer én URI af filen&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Repræsenterer flere URI&apos;s&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Luk ikke terminalen efter kommandoen er udført</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Udfør i terminalemulator</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Brug altid dette program til at åbne denne filtype</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Rediger bogmærker</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Navn</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Sted</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Tilføj bogmærke</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>&amp;Fjern bogmærke</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Brug træk- og-slip for at arrangere bogmærkerne</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Kør fil</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Åbn</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>&amp;Kør</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Kør i &amp;terminal</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Annuller</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Placering:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Filnavn:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Filtype:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Destination:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Arbejder:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Forbereder...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Fremskridt</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Tid tilbage:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Filer behandlet:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Filegenskaber</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Generelt</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Placering:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Filtype:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>MIME-type:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Filstørrelse:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Størrelse på disk:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Sidst ændret:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Henviser til:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Åbn med:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Senest tilgået:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation>Indeholder:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>Enhedsforbrug:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Tilladelser</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Ejerskab</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Gruppe:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Ejer:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Filrettigheder</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Andre:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Gør filen eksekverbar</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Læs</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Skriv</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Eksekver</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Sticky</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>SetUID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>SetGID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Avanceret</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Brugerdefineret</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Vælg et program til at åbne filer af typen &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Mappe</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Tom fil</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>Den specifcerede mappe &apos;%1&apos; er ikke gyldig</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Åbner...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Ingen undermapper&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Åbn i nyt &amp;faneblad</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Åbn i nyt &amp;vindue</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Åbn i termina&amp;l</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Kopier her</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Flyt her</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Opret symbolsk henvisning her</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Annuller</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Nyt bogmærke</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Filen &apos;%1&apos; ser ud til at være en skrivebordspost.
+Hvad vil du gøre med den?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Denne tekstfil &quot;%1&quot; er tilsyneladende et eksekverbart script.
+Hvad vil du foretage med det?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Filen &quot;%1&quot; er eksekverbar. Vil du eksekvere den?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Gå tilbage</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Venstre</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Gå fremad</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Højre</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Genindlæs</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Opret mappe</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Ikonvisning</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Miniaturevisning</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Kompakt visning</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Detaljeret liste visning</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Fejl</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Vælg venligst en fil</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 findes allerede.
+Vil du erstatte den?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Stien &quot;%1&quot; findes ikke</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>&quot;%1&quot; er ikke en mappe</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>&quot;%1&quot; er ikke en fil</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Åbn</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;Gem</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Alle filer (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Åbn fil</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Gem fil</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Åbn</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Åbn med...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Andre programmer</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Opret &amp;ny</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Gendan</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Klip</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Kopiér</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Indsæt</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>&amp;Smid i papirkurven</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Omdøb</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Udpak til...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Udpak her</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Komprimer</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Egenskaber</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Hav tillid til valgte eksekverbare</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Hav tillid til denne eksekverbar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Resultat</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Slet</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Fejl</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Nogle filer kan ikke smides i papirkurven, fordi det underliggende filsystem ikke understøtter denne funktion.
+Vil du slette dem istedet?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Bekræft</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Vil du slette de valgte filer?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Vil du smide de valgte filer i papirkurven?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Flyt filer</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Flytter følgende filer til destinationsmappen:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Kopier filer</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Kopier følgende filer til destinationsmappen:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Smid filer i papirkurven</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Flytter følgende filer til papirkurven:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Slet filer</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Sletter følgende filer:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Opret symbolske henvisninger</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Opretter symbolske henvisninger for følgende filer:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Skift filegenskaber</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Skifter filegenskaber for følgende filer:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Gendan filer fra papirkurven</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Gendanner følgende filer fra papirkurven:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Fejl</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Vis mappens indhold</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Vis og ændre mappens indhold</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Læs</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Læs og skriv</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Forbudt</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Filer af forskellig type</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Flere filer</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>%p% anvendt</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>%1 ledig af %2</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>ingen fil</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation>en fil</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation>%1 filer</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Vælg et ikon</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Billeder (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Anvend ændringer</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Vil du anvende disse ændringer rekursivt på alle filer og undermapper?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Fejl</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Du skal mindst vælge en mappe at søge i.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Vælg en mappe</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Kan ikke oprette et link på filsystem som ikke er indbygget</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Opret &amp;ny</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Indsæt</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Vælg &amp;alle</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Inventer markering</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Sortering</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Vis skjulte</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Mappe&amp;egenskaber</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Resultat</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Efter filnavn</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Efter ændringstidspunkt</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Efter filstørrelse</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Efter filtype</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Efter filejer</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Stigende</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Faldende</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Mapper først</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Forskel på store og små bogstaver</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Navn</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Type</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Størrelse</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Ændringstidspunkt</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Ejer</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Gruppe</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Fed</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Kursiv</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Forbind</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Rediger sti</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Kopiér sti</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Steder</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Skrivebord</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Computer</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Programmer</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Netværk</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Enheder</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Bogmærker</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Papirkurv</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Åbn i nyt faneblad</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Åbn i nyt vindue</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Tøm papirkurven</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Skjul</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Flyt bogmærke op</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Flyt bogmærke ned</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Omdøb bogmærke</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Fjern bogmærke</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Afmonter</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Monter</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Skub ud</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Vis alle poster</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Type: %1
+Størrelse: %2
+Ændringstidspunkt: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Type: %1
+Senset ændret: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>Over&amp;skriv</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Omdøb</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Steder</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Mappetræ</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Kan ikke fortryde at filen &apos;%s&apos; blev flyttet til papirkurven: oprindelig sti er ukendt</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Monter</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Fobind &amp;anonymt</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Forbind som &amp;bruger:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>&amp;Brugernavn:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Kodeord:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Domæne:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Glem kodeordet ø&amp;jeblikkeligt</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Husk kodeordet til du &amp;logger ud</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Husk &amp;for evigt</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Omdøb fil</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Indtast venligst et nyt navn:</translation>
+    </message>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Fejl</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Opret mappe</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Opret fil</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Indtast venligst et nyt filnavn:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Ny tekstfil</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Indtast venligst et nyt mappenavn:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Ny mappe</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Indtast et navn til det nye %1:</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Fejl ved brugerdefineret ikon</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Stien er ikke monteret.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Ugyldig skrivebordspost-fil: &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Der er ikke sat noget standardprogram til at starte &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Kan ikke sætte arbejdsmappe til &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation>Identifikator: </translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Bekræft for at erstatte filer</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Der er allerede en fil med dette navn i denne mappe.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Vil  du erstatte den eksisterende fil?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>Destination</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>med den følgende fil?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>Kildefilinfo</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>Dest. filinfo</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>kilde</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>&amp;Filnavn:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Gør dette for alle eksisterende filer</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Søg filer</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Navn/Sted</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Filnavnsmønster:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Ingen forskel på store og små bogstaver</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Brug regulære udtryk</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Mapper at søge i:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Tilføj</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Fjern</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Søg i undermapper</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Søg efter skjulte filer</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Filtype</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Søg kun efter følgende typer:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Tekstfiler</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Billeder</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Lydfiler</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Videofiler</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Dokumenter</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Mapper</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Indhold</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Filen indeholder:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Ingen forskel på store og små &amp;bogstaver</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>Bruger &amp;regulære udtryk</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Egenskaber</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Filstørrelse:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Større end:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Bytes</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Mindre end:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Sidst ændret:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Tidligere end:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Senere end:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_de.ts b/src/translations/libfm-qt_de.ts
new file mode 100644 (file)
index 0000000..7056a4c
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="de" sourcelanguage="en">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Wählen Sie eine Anwendung</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Installierte Anwendungen</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Benutzerdefinierter Befehl</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Auszuführende Befehlszeile:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Anwendungsname:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Diese speziellen Kürzel können im Befehl verwendet werden&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Repräsentiert einen einzelnen Dateinamen&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Repräsentiert mehrere Dateiennamen&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Repräsentiert eine einzelne URI einer Datei&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Repräsentiert mehrere URIs&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Terminalfenster nach der Ausführung des Befehls offen lassen</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>In einem Terminalemulator ausführen</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Ausgewählte Anwendung als Standardaktion für diesen Dateityp festlegen</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Lesezeichen bearbeiten</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Name</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Ort</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>Element &amp;hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>Element &amp;entfernen</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Benutzen Sie Drag&amp;Drop, um Elemente zu sortieren</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Datei ausführen</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>Ö&amp;ffnen</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>&amp;Ausführen</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Im &amp;Terminal ausführen</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Abbrechen</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Ort:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Dateiname:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Dateityp:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Ziel:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Verarbeite:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Vorbereiten...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Fortschritt</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Verbleibende Zeit:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Dateien verarbeitet:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Dateieigenschaften</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Allgemein</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Ort:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Dateityp:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>MIME-Typ:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Dateigröße:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Größe auf dem Datenträger:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Zuletzt geändert:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Verknüpfungsziel:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Öffnen mit:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Letzter Zugriff:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation>Enthält:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>Gerätenutzung:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Berechtigungen</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Besitz</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Gruppe:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Besitzer:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Zugriffskontrolle</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Andere:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Datei ausführbar machen</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Lesen</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Schreiben</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Ausführen</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>&quot;Sticky bit&quot;</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>SetUID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>SetGID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Erweiterter Modus</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Anpassen</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Wählen Sie eine Anwendung zum Öffnen von Dateien des Typs &quot;%1&quot; aus</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Ordner</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Leere Datei</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>Das angegebene Verzeichnis &apos;%1&apos; ist nicht gültig</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Laden...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Keine Unterverzeichnisse&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Öffnen in neuem &amp;Reiter</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>In neuem &amp;Fenster öffnen</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Im Termina&amp;l öffnen</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Hierhin kopieren</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Hierhin verschieben</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Hier eine symbolische Verknüpfung erstellen</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Abbrechen</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Neues Lesezeichen</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Diese Datei &apos;%1&apos; scheint ein Schreibtischeintrag zu sein.
+Was möchten Sie damit tun?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Diese Textdatei &apos;%1&apos; scheint ein ausführbares Skript zu sein.
+Was möchten Sie damit tun?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Diese Datei &apos;%1&apos; ist ausführbar. Möchten Sie sie ausführen?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Zurück</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Links</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Vorwärts</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Rechts</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Neu laden</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Ordner erstellen</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Symbolansicht</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Miniaturansicht</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Kompaktansicht</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Detaillierte Listenansicht</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Fehler</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Bitte wählen Sie eine Datei aus</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 existiert bereits.
+Möchten Sie sie ersetzen?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Pfad &quot;%1&quot; existiert nicht</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>&quot;%1&quot; ist kein Verzeichnis</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>&quot;%1&quot; ist keine Datei</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Öffnen</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;Speichern</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Alle Dateien (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Datei öffnen</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Datei speichern</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Öffnen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>&amp;Neu erstellen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>Wiede&amp;rherstellen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Ausschneiden</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Kopieren</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Einfügen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>In den Papierkorb &amp;verschieben</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Ausgewählten ausführbaren Dateien vertrauen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Dieser ausführbaren Datei vertrauen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Ausgabe</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Löschen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Umbenennen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Öffnen mit...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Andere Anwendungen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Entpacken nach...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Hier entpacken</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Komprimieren</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Eigenschaften</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Fehler</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Einige Dateien können nicht in den Papierkorb verschoben werden, da die zugrundeliegenden Dateisysteme den Vorgang nicht unterstützen.
+Möchtest Sie sie stattdessen löschen?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Bestätigen</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Möchten Sie die ausgewählten Dateien löschen?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Möchten Sie die ausgewählten Dateien in den Papierkorb verschieben?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Dateien verschieben</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Verschiebe die folgenden Dateien in den Zielordner:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Dateien kopieren</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Kopiere die folgenden Dateien in den Zielordner:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Dateien für den Papierkorb</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Verschiebe die folgenden Dateien in den Papierkorb:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Dateien löschen</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Lösche die folgenden Dateien:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Symbolische Verknüpfungen erstellen</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Erstelle symbolische Verknüpfungen für die folgenden Dateien:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Eigenschaften ändern</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Ändere die Eigenschaften der folgenden Dateien:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Dateien aus dem Papierkorb wiederherstellen</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Stelle folgende Dateien aus dem Papierkorb wieder her:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Fehler</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Ordnerinhalt ansehen</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Ordnerinhalt ansehen und verändern</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Lesen</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Lesen und Schreiben</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Unzulässig</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Dateien unterschiedlicher Typen</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Mehrere Dateien</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>%p% benutzt</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>%1 frei von %2</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>keine Datei</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation>eine Datei</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation>%1 Dateien</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Wählen Sie ein Symbol aus</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Bilder (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Änderungen anwenden</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Möchten Sie diese Änderungen rekursiv auf alle Dateien und Unterordner anwenden?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Fehler</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Sie sollten mindestens ein Verzeichnis zur Suche hinzufügen.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Wählen Sie einen Ordner aus</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Kann keine Verknüpfung auf einem nicht-nativen Dateisystem erstellen</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>&amp;Neu erstellen</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Einfügen</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>&amp;Alles auswählen</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Auswahl umkehren</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Sortierung</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Versteckte anzeigen</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>&amp;Ordnereigenschaften</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Ausgabe</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Nach Dateiname</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Nach Änderungszeit</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Nach Dateigröße</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Nach Dateityp</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Nach Dateibesitzer</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Aufsteigend</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Absteigend</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Ordner zuerst</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Groß-/ Kleinschreibung beachten</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Name</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Typ</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Größe</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Geändert</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Besitzer</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Gruppe</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Fett</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Kursiv</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Verbinden</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>Pfad b&amp;earbeiten</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>Pfad &amp;kopieren</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Orte</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Schreibtisch</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Papierkorb</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Rechner</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Anwendungen</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Netzwerk</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Geräte</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Lesezeichen</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Papierkorb leeren</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>In neuem Tab öffnen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>In neuem Fenster öffnen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Ausblenden</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Lesezeichen nach oben verschieben</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Lesezeichen nach unten verschieben</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Lesezeichen umbenennen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Lesezeichen entfernen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Aushängen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Einhängen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Auswerfen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Alle Einträge anzeigen</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Typ: %1
+Größe: %2
+Geändert: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Typ: %1
+Geändert: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>&amp;Überschreiben</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Umbenennen</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Orte</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Verzeichnisbaum</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Datei &apos;1%&apos; ist aus dem Papierkorb nicht wieder herstellbar: ursprünglicher Pfad nicht bekannt</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Einhängen</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>&amp;Anonym verbinden</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Als &amp;Benutzer verbinden:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>Ben&amp;utzername:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Passwort:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Domäne:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Passwort &amp;sofort vergessen</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Passwort bis zum Abme&amp;lden merken</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Passwort &amp;für immer merken</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Fehler</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Datei umbenennen</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Bitte geben Sie einen neuen Namen ein:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Ordner erstellen</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Bitte geben Sie einen neuen Dateinamen ein:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Neue Textdatei</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Bitte geben Sie einen neuen Ordnernamen ein:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Neuer Ordner</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Geben Sie einen Namen für %1 ein:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Datei erstellen</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Benutzerdefiniertes-Symbol Fehler</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Der Pfad ist nicht eingehängt.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Ungültige Schreibtischeintragsdatei: &apos;1%&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Es ist keine Standardanwendung zum Starten von &apos;%1&apos; festgelegt</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Arbeitsverzeichnis kann nicht auf &apos;%1&apos; festgelegt werden: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation>Bezeichner: </translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Überschreiben von Dateien bestätigen</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Es gibt bereits eine gleichnamige Datei an diesem Ort.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Möchten Sie die vorhandene Datei ersetzen?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>Ziel</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>mit der folgenden Datei?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>Info über die Quelldatei</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>Info über die Zieldatei</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>Quelle</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>&amp;Dateiname:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Diese Option auf alle existierenden Dateien anwenden</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Dateien suchen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Name/Ort</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Muster für Dateinamen:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Groß-/Kleinschreibung ignorieren</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Regulären Ausdruck verwenden</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Zu durchsuchende Orte:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>Entfe&amp;rnen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>In Unterverzeichnissen suchen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Nach versteckten Dateien suchen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Dateityp</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Nur nach Dateien der folgenden Typen suchen:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Textdateien</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Bilddateien</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Audiodateien</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Videodateien</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Dokumente</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Ordner</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Inhalt</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Datei enthält:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Groß- /Kleinschreibung ig&amp;norieren</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>Reg&amp;ulären Ausdruck verwenden</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Eigenschaften</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Dateigröße:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Größer als:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Bytes</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Kleiner als:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Letzte Änderungszeit:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Früher als:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Später als:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_el.ts b/src/translations/libfm-qt_el.ts
new file mode 100644 (file)
index 0000000..f42e579
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="el">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Επιλέξτε μια εφαρμογή</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Εγκατεστημένες εφαρμογές</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Προσαρμοσμένη εντολή</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Γραμμή εντολών προς εκτέλεση:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Όνομα της εφαρμογής:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Αυτοί οι ειδικοί κωδικοί μπορούν να χρησιμοποιηθούν στη γραμμή εντολών:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Αναπαριστά ένα όνομα αρχείου&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Αναπαριστά πολλαπλά ονόματα αρχείων&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Αναπαριστά ένα URI του αρχείου&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Αναπαριστά πολλαπλά URI&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Διατήρηση του παραθύρου του τερματικού ανοιχτό μετά την εκτέλεση της εντολής</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Εκτέλεση στον προσομοιωτή τερματικού</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Ορίστε την επιλεγμένη εφαρμογή ως την εξ ορισμού ενέργεια για αυτού του τύπου αρχεία</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Επεξεργασία σελιδοδεικτών</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Όνομα</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Τοποθεσία</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Προσθήκη αντικειμένου</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>&amp;Αφαίρεση αντικειμένου</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Χρησιμοποιήστε τη μεταφορά και απόθεση για αναδιάταξη των αντικειμένων</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Εκτέλεση του αρχείου</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>Ά&amp;νοιγμα</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>&amp;Εκτέλεση</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Εκτέλεση στο &amp;τερματικό</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Ακύρωση</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Τοποθεσία:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Όνομα αρχείου:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Τύπος αρχείου:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Προορισμός:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Επεξεργασία:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Προετοιμασία...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Πρόοδος</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Υπολειπόμενος χρόνος:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Επεξεργασμένα αρχεία:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Ιδιότητες του αρχείου</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Γενικά</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Τοποθεσία:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Τύπος αρχείου:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Τύπος Mime:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Μέγεθος αρχείου:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Μέγεθος στον δίσκο:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Τελευταία τροποποίηση:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Προορισμός συνδέσμου:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Άνοιγμα με:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Τελευταία προσπέλαση:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation>Περιέχει:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>Χρήση συσκευής:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Άδειες</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Ιδιοκτησία</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Ομάδα:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Ιδιοκτήτης:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Έλεγχος πρόσβασης</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Άλλο:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Ορισμός ως εκτελέσιμο</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Ανάγνωση</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Εγγραφή</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Εκτέλεση</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Κολλημένο</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>SetUID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>SetGID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Προηγμένη λειτουργία</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Προσαρμοσμένο</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Επιλέξτε μια εφαρμογή για το άνοιγμα των αρχείων «%1»</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Φάκελος</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Κενό αρχείο</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>Ο κατάλογος &apos;%1&apos; δεν είναι έγκυρος</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Φόρτωση...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Κανένας υποφάκελος&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Άνοιγμα σε νέα &amp;καρτέλα</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Άνοιγμα σε νέο &amp;παράθυρο</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Άνοιγμα στο &amp;τερματικό</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Αντιγραφή εδώ</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Μετακίνηση εδώ</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Δημιουργία συμβολικού δεσμού εδώ</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Ακύρωση</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Νέος σελιδοδείκτης</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Το αρχείο «%1» φαίνεται να είναι ένα αρχείο επιφάνειας εργασίας.
+Ποια ενέργεια θέλετε να πραγματοποιήσετε;</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Το αρχείο κειμένου «%1» φαίνεται ότι είναι ένα εκτελέσιμο σενάριο.
+Τι θέλετε να κάνετε με αυτό;</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Αυτό το αρχείο «%1» είναι εκτελέσιμο. Θέλετε να το εκτελέσετε;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Μετάβαση πίσω</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Αριστερά</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Μετάβαση εμπρός</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Δεξιά</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Επαναφόρτωση</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Δημιουργία φακέλου</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Προβολή εικονιδίων</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Προβολή εικόνων επισκόπησης</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Συμπαγής προβολή</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Προβολή αναλυτικού κατάστιχου</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Σφάλμα</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Παρακαλώ επιλέξτε ένα αρχείο</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>Το αρχείο %1 υπάρχει ήδη.
+Θέλετε να το αντικαταστήσετε;</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Η διαδρομή «%1» δεν υπάρχει</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>Το «%1» δεν είναι κατάλογος</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>Το «%1» δεν είναι αρχείο</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>Ά&amp;νοιγμα</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>Απο&amp;θήκευση</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Όλα τα αρχεία (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Άνοιγμα αρχείου</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Αποθήκευση αρχείου</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Άνοιγμα</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Άνοιγμα με...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Άλλες εφαρμογές</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Δημιουργία &amp;νέου</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Επαναφορά</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Αποκοπή</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Αντιγραφή</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Επικόλληση</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>&amp;Μετακίνηση στα απορρίμματα</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Μετονομασία</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Εξαγωγή σε...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Εξαγωγή εδώ</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Συμπίεση</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Ιδιότητες</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Τα επιλεγμένα εκτελέσιμα είναι έμπιστα</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Το εκτελέσιμο είναι έμπιστο</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Έξοδος</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Διαγραφή</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Σφάλμα</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Μερικά αρχεία δεν μπορούν να μετακινηθούν στον κάδο απορριμμάτων διότι το υποκείμενο αρχείο συστήματος δεν υποστηρίζει αυτήν την ενέργεια.
+Θέλετε αντί αυτού να τα διαγράψετε;</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Επιβεβαίωση</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Επιθυμείτε την διαγραφή των επιλεγμένων αρχείων;</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Επιθυμείτε την μετακίνηση των επιλεγμένων αρχείων στον κάδο απορριμμάτων;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Μετακίνηση των αρχείων</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Μετακίνηση των ακολούθων αρχείων στον φάκελο προορισμού:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Αντιγραφή των αρχείων</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Αντιγραφή των ακολούθων αρχείων στον φάκελο προορισμού:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Ρίψη των αρχείων στα απορρίμματα</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Μετακίνηση των ακολούθων αρχείων στον κάδο απορριμμάτων:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Διαγραφή των αρχείων</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Διαγραφή των ακολούθων αρχείων:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Δημιουργία συμβολικών δεσμών</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Δημιουργία συμβολικών δεσμών για τα ακόλουθα αρχεία:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Αλλαγή ιδιοχαρακτηριστικών</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Αλλαγή των ιδιοχαρακτηριστικών των ακολούθων αρχείων:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Επαναφορά των αρχείων από τον κάδο απορριμμάτων</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Επαναφέρονται τα παρακάτω αρχεία από τον κάδο απορριμμάτων:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Σφάλμα</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Προβολή των περιεχομένων του φακέλου</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Προβολή και τροποποίηση των περιεχομένων του φακέλου</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Ανάγνωση</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Ανάγνωση και εγγραφή</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Απαγορευμένο</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Αρχεία διαφορετικού τύπου</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Πολλαπλά αρχεία</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>%p% σε χρήση</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>%1 Ελεύθερα από %2</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>κανένα αρχείο</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation>ένα αρχείο</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation>%1 αρχεία</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Επιλογή εικονιδίου</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Εικόνες (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Εφαρμογή των αλλαγών</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Θέλετε να εφαρμόσετε αναδρομικά αυτές τις αλλαγές σε όλα τα αρχεία και υποφακέλους;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Σφάλμα</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Για την αναζήτηση θα πρέπει να προσθέσετε τουλάχιστον έναν κατάλογο.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Επιλογή φακέλου</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Αδύνατη η δημιουργία δεσμού σε μη εγγενές σύστημα αρχείων</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Δημιουργία &amp;νέου</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>Επι&amp;κόλληση</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Επιλογή ό&amp;λων</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Αντιστροφή επιλογής</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Ταξινόμηση</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Εμφάνιση των κρυφών</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Ι&amp;διότητες του φακέλου</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Έξοδος</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Ανά όνομα αρχείου</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Ανά χρόνο τροποποίησης</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Ανά μέγεθος αρχείου</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Ανά τύπο αρχείου</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Ανά ιδιοκτήτη αρχείου</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Αύξουσα</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Φθίνουσα</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Οι φάκελοι πρώτα</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Διάκριση πεζών/κεφαλαίων</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Όνομα</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Τύπος</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Μέγεθος</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Τροποποιήθηκε</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Ιδιοκτήτης</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Ομάδα</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Έντονα</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Πλάγια</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Σύνδεση</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Επεξεργασία της διαδρομής</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Αντιγραφή της διαδρομής</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Τοποθεσίες</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Επιφάνεια εργασίας</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Υπολογιστής</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Εφαρμογές</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Δίκτυο</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Συσκευές</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Σελιδοδείκτες</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Απορρίμματα</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Άνοιγμα σε νέα καρτέλα</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Άνοιγμα σε νέο παράθυρο</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Άδειασμα των απορριμμάτων</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Απόκρυψη</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Μετακίνηση του σελιδοδείκτη προς τα πάνω</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Μετακίνηση του σελιδοδείκτη προς τα κάτω</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Μετονομασία σελιδοδείκτη</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Αφαίρεση σελιδοδείκτη</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Αποπροσάρτηση</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Προσάρτηση</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Εξαγωγή</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Εμφάνιση όλων των καταχωρήσεων</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Τύπος: %1
+Μέγεθος: %2
+Τροποποιήθηκε: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Τύπος: %1
+Τροποποιήθηκε: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>&amp;Αντικατάσταση</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Μετονομασία</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Τοποθεσίες</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Δέντρο καταλόγων</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Αδύνατη η επαναφορά του αρχείου «%s» από τα απορρίμματα: άγνωστη αρχική διαδρομή</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Προσάρτηση</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>&amp;Ανώνυμη σύνδεση</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Σύνδεση ως &amp;χρήστης:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>Όνομα χ&amp;ρήστη:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Κωδικός πρόσβασης:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Τομέας:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>&amp;Λήθη του κωδικού πρόσβασης άμεσα</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Απομνημόνευση του κωδικού πρόσβασης &amp;μέχρι να αποσυνδεθείτε</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Απομνημόνευση ε&amp;σαεί</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Μετονομασία αρχείου</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Παρακαλώ εισαγάγετε ένα νέο όνομα:</translation>
+    </message>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Σφάλμα</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Δημιουργία φακέλου</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Δημιουργία αρχείου</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Παρακαλώ εισαγάγετε ένα νέο όνομα αρχείου:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Νέο αρχείο κειμένου</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Παρακαλώ εισαγάγετε ένα νέο όνομα φακέλου:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Νέος φάκελος</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Εισαγάγετε ένα όνομα για το νέο %1:</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Σφάλμα προσαρμοσμένου εικονιδίου</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Η διαδρομή δεν έχει προσαρτηθεί.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Μη έγκυρο αρχείο επιφάνειας εργασίας: «%1»</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Καμία εξ ορισμού εφαρμογή για την εκτέλεση του &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Αδύνατος ο ορισμός του καταλόγου εργασίας «%1»: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation>Αναγνωριστικό: </translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Επιβεβαίωση αντικατάστασης των αρχείων</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Υπάρχει ήδη ένα αρχείο με το ίδιο όνομα στην συγκεκριμένη τοποθεσία.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Θέλετε να αντικαταστήσετε το υπάρχον αρχείο;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>προορισμός</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>με το παρακάτω αρχείο;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>πληροφορίες αρχείου πηγής</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>πληροφορίες αρχείου προορισμού</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>πηγή</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>&amp;Όνομα αρχείου:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Εφαρμογή της επιλογής σε όλα τα υπάρχοντα αρχεία</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Αναζήτηση αρχείων</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Όνομα/Τοποθεσία</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Σχηματομορφές ονομάτων αρχείων:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Δίχως διάκριση πεζών/κεφαλαίων</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Χρήση κανονικής έκφρασης</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Τοποθεσίες προς αναζήτηση:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Προσθήκη</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>Α&amp;φαίρεση</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Αναζήτηση σε υποφακέλους</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Αναζήτηση για κρυφά αρχεία</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Τύπος αρχείου</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Αναζήτηση αρχείων μόνο για τους ακόλουθους τύπους:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Αρχεία κειμένου</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Αρχεία εικόνων</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Αρχεία ήχου</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Αρχεία βίντεο</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Έγγραφα</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Φάκελοι</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Περιεχόμενο</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Αρχεία που περιέχουν:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Δίχως διάκριση &amp;πεζών/κεφαλαίων</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>&amp;Χρήση κανονικής έκφρασης</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Ιδιότητες</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Μέγεθος αρχείου:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Μεγαλύτερο από:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Byte</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Μικρότερο από:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Χρόνος της τελευταίας τροποποίησης:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Προγενέστερα από:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Αργότερα από:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_es.ts b/src/translations/libfm-qt_es.ts
new file mode 100644 (file)
index 0000000..4bc7288
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="es">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Elija una aplicación</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Aplicaciones instaladas</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Comando personalizado</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Línea de comandos a ejecutar:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Nombre de la aplicación:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Estos códigos especiales pueden ser usados en la línea de comandos:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Representa un solo nombre de archivo&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Representa múltiples nombres de archivos&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Representa un solo URI del archivo&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Representa múltiples URI&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Mantener la terminal abierta tras la ejecución del comando</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Ejecutar en un emulador de terminal</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Establecer la aplicación seleccionada como predefinida para este tipo de archivo</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Editar marcadores</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Nombre</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Ubicación</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Añadir elemento</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>&amp;Eliminar elemento</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Arrastre y suelte para reordenar los elementos</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Ejecutar archivo</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Abrir</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>&amp;Ejecutar</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Ejecutar en una &amp;terminal</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Cancelar</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Ubicación:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Nombre del archivo:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Tipo de archivo:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Destino:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Procesando:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Preparando...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Progreso</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Tiempo restante:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Archivos procesados:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Propiedades del archivo</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>General</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Ubicación:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Tipo de archivo:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Tipo MIME:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Tamaño del archivo:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Tamaño en disco:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Última modificación:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Destino del enlace:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Abrir con:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Último acceso:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation>Contiene:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>Uso del dispositivo:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Permisos</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Propiedad</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Grupo:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Propietario:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Control de acceso</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Otros:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Hacer el archivo ejecutable</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Lectura</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Escritura</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Ejecución</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Pegajoso</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>SetUID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>SetGID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Modo avanzado</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Personalizar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Seleccione una aplicación para abrir archivos «%1»</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Carpeta</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Archivo vacío</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>El directorio dado «%1» no es válido</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Cargando...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;No hay subdirectorios&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Abrir en una pes&amp;taña nueva</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Abrir en una ventana nueva</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Abrir en una termina&amp;l</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Copiar aquí</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Mover aquí</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Crear enlace simbólico aquí</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Cancelar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Nuevo marcador</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>El archivo «%1» parece ser un lanzador de programa («desktop entry»).
+¿Qué quiere hacer con él?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>El archivo de texto «%1» parece ser un guion ejecutable.
+¿Qué desea hacer con él?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>El archivo «%1» es ejecutable. ¿Quiere ejecutarlo?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Retroceder</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Izquierda</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Avanzar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Derecha</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Recargar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Crear carpeta</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Vista de iconos</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Vista de miniaturas</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Vista compacta</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Vista de lista detallada</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Seleccione un archivo</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>Ya existe %1.
+¿Quiere sobrescribirlo?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>No existe la ruta «%1»</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>«%1» no es un directorio</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>«%1» no es un archivo</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Abrir</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>Guardar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Todos los archivos (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Abrir un archivo</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Guardar el archivo</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Abrir</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Abrir con...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Otras aplicaciones</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Crear &amp;nuevo</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Restaurar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Cortar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Copiar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Pegar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>&amp;Mover a la papelera</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Renombrar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Extraer en...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Extraer aquí</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Comprimir</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Propiedades</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Confiar en los ejecutables seleccionados</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Confiar en este ejecutable</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Salida</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Borrar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Algunos archivos no pueden moverse a la papelera porque los sistemas de archivos subyacentes no permiten esta operación.
+¿Quiere eliminarlos en vez de moverlos?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Confirmación</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>¿Quiere borrar los archivos seleccionados?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>¿Quiere mover los archivos seleccionados a la papelera?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Mover archivos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Moviendo los siguientes archivos al directorio de destino:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Copiar archivos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Copiando los siguientes archivos al directorio de destino:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Enviar los archivos a la papelera</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Moviendo los siguientes archivos a la papelera:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Borrar los archivos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Borrando los siguientes archivos:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Crear enlaces simbólicos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Creando enlaces simbólicos para los siguientes archivos:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Cambiar atributos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Cambiando atributos para los siguientes archivos:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Restaurar archivos de la papelera</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Restaurando los siguientes archivos desde la papelera:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Ver el contenido de la carpeta</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Ver y modificar el contenido de la carpeta</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Lectura</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Lectura y escritura</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Prohibido</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Archivos de diferentes tipos</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Múltiples archivos</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>%p% usado</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>%1 libre(s) de %2</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>ningún archivo</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation>un archivo</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation>%1 archivos</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Seleccione un icono</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Imágenes (*.png *.xpm *.svg *.svgz)</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Aplicar los cambios</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>¿Quiere aplicar los cambios a todos los archivos y subdirectorios?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Debe añadir al menos un directorio donde buscar.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Seleccione una carpeta</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>No se pueden crear enlaces en sistemas de archivos no nativos</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Crear &amp;nuevo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Pegar</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Seleccion&amp;ar todo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Invertir la selección</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Ordenar</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Mostrar ocultos</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Pr&amp;opiedades del directorio</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Salida</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Por nombre de archivo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Por fecha de modificación</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Por tamaño de archivo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Por tipo de archivo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Por propietario del archivo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Ascendente</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Descendente</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Primero las carpetas</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Distinguir mayúsculas de minúsculas</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Nombre</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Tipo</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Tamaño</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Modificado</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Propietario</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Grupo</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Negrita</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Cursiva</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Conectar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Editar la ruta</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Copiar la ruta</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Lugares</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Escritorio</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Sistema</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Aplicaciones</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Red</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Dispositivos</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Marcadores</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Papelera</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Vaciar la papelera</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Abrir en una pestaña nueva</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Abrir en una ventana nueva</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Ocultar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Mover el marcador hacia arriba</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Mover el marcador hacia abajo</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Renombrar el marcador</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Eliminar el marcador</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Desmontar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Montar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Expulsar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Mostrar todas las entradas</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Tipo: %1
+Tamaño: %2
+Modificado: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Tipo: %1
+Modificado: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>&amp;Sobrescribir</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Renombrar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Lugares</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Árbol de directorios</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>No se puede restaurar el archivo «%s» desde la papelera: la ruta original es desconocida</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Montar</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Conectar &amp;anónimamente</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Conectar como el u&amp;suario:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>&amp;Usuario:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Contraseña:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Dominio:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Olvidar la contraseña &amp;inmediatamente</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>&amp;Recordar la contraseña hasta cerrar la sesión</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Recordar &amp;para siempre</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Error</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Renombrar archivo</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Introduzca un nuevo nombre:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Crear carpeta</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Introduzca un nuevo nombre de archivo:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Nuevo archivo de texto</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Introduzca el nuevo nombre de la carpeta:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Nueva carpeta</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Introduzca un nombre para el nuevo %1:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Crear archivo</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Icono de error personalizado</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>La ruta no está montada.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Archivo lanzador (desktop entry) no válido: «%1»</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>No hay ninguna aplicación por defecto configurada para lanzar «%1»</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>No se puede cambiar el directorio de trabajo a «%1»: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation>Identificador: </translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Pedir confirmación al sustituir archivos</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Ya existe un archivo con el mismo nombre en este lugar.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;¿Quiere reemplazar el archivo existente?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>destino</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>con el siguiente archivo?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>información del archivo de origen</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>información del archivo de destino</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>origen</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>Nombre del &amp;archivo:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Aplicar esta opción a todos los archivos existentes</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Buscar archivos</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Nombre/Ubicación</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Patrones de nombre de archivo:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>No distinguir mayúsculas de minúsculas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Usar expresiones regulares</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Lugares donde buscar:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Añadir</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>Elimina&amp;r</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Buscar en subdirectorios</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Buscar archivos ocultos</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Tipo de archivo</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Buscar solo archivos de los siguientes tipos:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Archivos de texto</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Archivos de imagen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Archivos de sonido</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Archivos de vídeo</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Documentos</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Carpetas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Contenido</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>El archivo contiene:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>No &amp;distinguir mayúsculas de minúsculas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>&amp;Usar expresiones regulares</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Propiedades</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Tamaño del archivo:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Mayor que:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Bytes</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Menor que:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Fecha de la última modificación:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Anterior al:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Posterior al:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_fr.ts b/src/translations/libfm-qt_fr.ts
new file mode 100644 (file)
index 0000000..df01204
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="fr">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Sélectionnez une application</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Applications installées</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Commandes personnalisées</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Ligne de commande à exécuter:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Nom de l’application:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Ces codes spéciaux peuvent être utilisés dans la ligne de commande :&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Représente un seul nom de fichier&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Représente plusieurs noms de fichiers&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Représente l’URI d&apos;un seul fichier&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Représente plusieurs URIs&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Garder la fenêtre de terminal ouverte après exécution de la commande</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Exécuter dans un émulateur de terminal</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Définir l’application sélectionnée comme action par défaut pour les fichiers de ce type</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Modifier les signets</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Nom</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Emplacement</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Ajouter un élément</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>&amp;Supprimer un élément</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Utiliser le glisser-déposer pour trier à nouveau les éléments</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Exécuter le fichier</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Ouvrir</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>E&amp;xécuter</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Exécuter dans un &amp;terminal</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Annuler</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Emplacement:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Nom du fichier:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Type de fichier:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Destination&#xa0;:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>En Traitement:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>En Préparation…</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>En Progression</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Temps restant:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Fichiers traités:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Propriétés du fichier</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Général</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Emplacement:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Type de fichier:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Type MIME:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Taille du fichier:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Taille sur le disque:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Date de dernière modification:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Cible du lien:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Ouvrir avec:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Date de dernier accès:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Droits d&apos;accès</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Propriété</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Groupe:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Propriétaire:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Contrôle d’accès</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Autre:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Rendre le fichier exécutable</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Lecture</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Écriture</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Exécution</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Permanent</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>SetUID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>SetGID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Mode avancé</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Personnaliser</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Sélectionnez une application pour ouvrir les fichiers de type &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Dossier</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Fichier vide</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>Le répertoire spécifié &apos;%1&apos; n&apos;est pas valide</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Chargement…</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Aucun sous-dossiers&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Ouvrir dans un nouvel &amp;onglet</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Ouvrir dans une nouvelle &amp;fenêtre</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Ouvrir dans un &amp;terminal</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Copier ici</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Déplacer ici</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Créer un lien symbolique ici</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Annuler</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Nouveau signet</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Ce fichier &apos;%1&apos; semble être une entrée desktop.
+Que voulez-vous faire avec ?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Le fichier texte &apos;%1&apos; semble être un script exécutable.
+Qu voulez-voulez vous en faire ?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Le fichier &apos;%1&apos; est exécutable. Souhaitez-vous le lancer ?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Précédent</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+flèche gauche</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Suite</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+flèche droite</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Recharger</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Créer un dossier</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Vue par icônes</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Vue par miniatures</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Vue compacte de la liste</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Vue détaillée de la liste</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Erreur</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Prière de sélectionner un fichier</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 existe déjà.
+Voulez-vous le remplacer ?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Le patch &quot;%1&quot; n&apos;existe pas</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>&quot;%1&quot; n&apos;est pas un répertoire</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>&quot;%1&quot; n&apos;est pas un fichier</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Ouvrir</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;Enregistrer</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Tous les fichiers (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Fichier ouvert</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Enregistrer le fichier</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Ouvrir</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Ouvrir avec…</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Autres applications</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Créer un &amp;nouveau</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Restaurer</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Couper</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Copier</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Coller</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>&amp;Mettre à la corbeille</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Renommer</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Extraire vers…</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Extraire ici</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Compresser</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Propriétés</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Faire confiance aux exécutables sélectionnés</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Faites confiance à cet exécutable</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Sortie</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Supprimer</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Erreur</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Certains fichiers ne peuvent pas être mis à la corbeille car les systèmes de fichiers sous-jacents ne prennent pas en charge cette opération.
+Voulez-vous plutôt les supprimer ?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Confirmer</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Voulez-vous supprimer les fichiers sélectionnés ?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Voulez-vous mettre les fichiers sélectionnés à la corbeille ?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Déplacer les fichiers</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Déplacement des fichiers suivants vers le dossier de destination en cours:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Copier les fichiers</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Copie des fichiers suivants vers le dossier de destination en cours:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Mettre les fichiers de la corbeille</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Déplacement des fichiers suivants vers la corbeille en cours:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Supprimer les fichiers</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Suppression des fichiers suivants:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Créer des liens symboliques</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Création de liens symboliques avec les fichiers suivants en cours:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Modifier les attributs</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Modification des attributs des fichiers suivants en cours:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Restaurer les fichiers de la corbeille</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Restauration des fichiers suivants depuis la corbeille en cours:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Erreur</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Voir le contenu du dossier</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Voir et modifier le contenu du dossier</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Lecture</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Lecture et écriture</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Interdit</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Fichiers de différents types</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Fichiers multiples</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Sélectionner une icône</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Images (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Appliquer les modifications</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Voulez-vous appliquer ces changements récursivement à tous les fichiers et sous-dossiers ?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Erreur</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Vous devriez indiquer au moins un dossier pour la recherche.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Sélectionnez un dossier</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Ne peut pas créer un lien vers un système de fichiers non natif</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Créer un &amp;nouveau</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Coller</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Sélectionner &amp;tous</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Inverser la sélection</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Tri en cours</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Afficher les éléments cachés</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Pr&amp;opriétés du dossier</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Sortie</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Par nom de fichier</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Par date de modification</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Par taille de fichier</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Par type de fichier</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Par propriétaire de fichier</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Ascendant</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Descendant</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Dossier en premier</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Sensible à la casse</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Nom</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Type</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Taille</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Modifié</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Propriétaire</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Groupe</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Gras</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Italique</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Connecter</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Editer le chemin</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Copier le chemin</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Emplacements</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Bureau</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Corbeille</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Ordinateur</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Applications</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Réseau</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Appareils</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Signets</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Vider la corbeille</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Ouvrir dans un nouvel onglet</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Ouvrir dans une nouvelle fenêtre</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Cacher</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Monter le signet</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Descendre le signet</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Renommer le signet</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Effacer le signet</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Démonter</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Monter</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Éjecter</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Afficher toutes les entrées</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Type: %1
+Taille: %2
+Modification: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Type: %1
+Modification: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>&amp;Remplacer</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Renommer</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Emplacements</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Arborescence des répertoires</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Ne peut pas sortir le fichier &apos;%s&apos; de la corbeille: le chemin d&apos;origine est inconnu</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Monter</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Connecter &amp;anonymement</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Connecter comme utilis&amp;sateur:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>Nom d&apos;&amp;utilisateur:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>Mot de &amp;passe:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Domaine:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Oublier le mot de passe &amp;immédiatement</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Se souvenir du mot de passe jusqu&apos;à &amp;la déconnexion</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>&amp;Toujours s&apos;en souvenir</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Erreur</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Renommer le fichier</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Veuillez entrer un nouveau nom:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Créer un dossier</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Veuillez entrer un nouveau nom de fichier:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Nouveau fichier texte</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Veuillez entrer un nouveau nom de répertoire:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Nouveau répertoire</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Entrez un nom pour le nouveau %1:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Créer un fichier</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Erreur avec l&apos;icône personnelle</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Le chemin n&apos;est pas monté.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Fichier d&apos;entrée desktop invalide &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Aucune application par défaut n&apos;est définie pour lancer &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Impossible de définir le répertoire de travail à &apos;%1&apos;: &apos;%2&apos;</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Confirmer le remplacement des fichiers</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Il existe déjà un fichier avec le même nom à cet emplacement.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Voulez-vous remplacer le fichier existant&#xa0;?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>dst</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>avec le fichier suivant ?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>infos fichier src</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>infos fichier dst</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>src</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>Nom de &amp;fichier&#xa0;:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Appliquer cette option à tous les fichiers existants</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Recherche de fichiers</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Nom/Emplacement</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Motifs dans le nom des fichiers:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Insensible à la casse</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Utiliser les expressions rationnelles</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Emplacements de la recherche:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Ajouter</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Supprimer</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Recherche dans les sous-répertoires</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Rechercher les fichiers cachés</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Type de fichier</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Rechercher uniquement les types de fichiers suivants:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Fichiers texte</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Fichiers image</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Fichiers audio</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Fichiers vidéo</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Documents</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Dossiers</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Contenu</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Le fichier contient:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>&amp;Insensible à la casse</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>&amp;Utiliser les expressions rationelles</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Propriétés</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Taille du fichier:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Plus grands que:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Octets</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Plus petits que:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Date de dernière modification:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Plus récent que:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Plus tard que:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_gl.ts b/src/translations/libfm-qt_gl.ts
new file mode 100644 (file)
index 0000000..2600dca
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="gl">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Escolla un aplicativo</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Aplicativos instalados</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Orde personalizada</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Liña de ordes a executar:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Nome do aplicativo:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Na liña de ordes pódense empregar estes códigos especiais:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Representa un só nome de ficheiro&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Representa múltiples nomes de ficheiros&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Representa un só URI do ficheiro&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Representa múltiples URI&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Manter a terminal aberta após  a execución da orde</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Executar nun emulador de terminal</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Estabelecer o aplicativo seleccionado como predeterminado para este tipo de ficheiro</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Editar marcadores</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Nome</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Localización</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Engadir un elemento</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>&amp;Retirar o elemento</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Usar arrastrar e soltar para ordenar elementos</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Executar o ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Abrir</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>E&amp;xecutar</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Executar nunha &amp;terminal</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Cancelar</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Localización:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Nome do ficheiro:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Tipo de ficheiro:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Destino:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Procesando:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Preparando...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>En progreso</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Tempo restante:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Ficheiros procesados:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Propiedades do ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Xeral</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Localización:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Tipo de ficheiro:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Tipo MIME:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Tamaño do ficheiro:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Tamaño no disco:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Última modificación:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Destino da ligazón:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Abrir con:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Último acceso:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation>Contén:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>Uso do dispositivo:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Permisos</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Pertenza</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Grupo:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Propietario:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Control de acceso</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Outros:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Facer que o ficheiro sexa executábel</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Lectura</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Escritura</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Execución</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Persistente</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>Estabelecer o UID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>Estabelecer o GID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Modo avanzado</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Personalizar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Seleccione un aplicativo para abrir os ficheiros «%1»</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Cartafol</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Ficheiro en branco</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>O directorio especificado «%1» non é válido</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Cargando...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;non hai subcartafoles&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Abrir nunha nova l&amp;apela</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Abrir nunha nova xa&amp;nela</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Abrir nun termina&amp;l</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Copiar para aquí</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Mover para aquí</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Crear aquí unha ligazón simbólica</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Cancelar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Novo marcador</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>O ficheiro «%1» semella ser unha entrada de escritorio.
+Que quere facer con el?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Este ficheiro de texto «%1» semella ser un script executábel.
+Que quere facer con el?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Este ficheiro «%1» é executábel. Quere executalo?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Volver atrás</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Esquerda</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Avanzar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Dereita</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Cargar de novo</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Crear un cartafol</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Vista en Iconas</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Vista de miniaturas</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Vista compacta</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Ver como lista detallada</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Seleccione un ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>Xa existe %1.
+Quere sobrescribilo?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Non existe a ruta «%1»</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>«%1» non é un directorio</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>«%1» non é un ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Abrir</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;Gardar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Todos os ficheiros (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Abrir ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Gardar o ficheiro</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Abrir</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Cortar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Copiar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Pegar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>Deitar no &amp;lixo</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Confiar nos executábeis seleccionados</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Confiar neste executábel</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Saída</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Eliminar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Renomear</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Abrir con...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Outros aplicativos</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Crear &amp;novo</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Restaurar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Extraer en…</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Extraer aquí</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Comprimir</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Propiedades</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Algúns ficheiros non poden enviarse ao cesto do lixo porque o subsistema de ficheiros non permite esta operación.
+Desexa eliminalos no seu canto?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Confirmar</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Quere eliminar os ficheiros seleccionados?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Quere mover os ficheiros seleccionados ao cesto do lixo?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Mover os ficheiros</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Movendo os seguintes ficheiros ao cartafol de destino:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Copiar os ficheiros</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Copiando os seguintes ficheiros ao cartafol de destino:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Deitar os ficheiros no lixo</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Movendo os seguintes ficheiros ao lixo:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Eliminar os ficheiros</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Eliminando os seguintes ficheiros:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Crear ligazóns simbólicas</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Creando ligazóns simbólicas para os seguintes ficheiros:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Cambiar os atributos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Cambiando os atributos dos seguintes ficheiros:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Restaurar os ficheiro do lixo</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Restaurando os seguintes ficheiros do lixo:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Ver o contido do cartafol</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Ver e modificar o contido do cartafol</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Lectura</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Lectura e escritura</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Prohibido</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Ficheiros de tipos diferentes</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Múltiplos ficheiros</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>%p% utilizado</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>%1 libre de %2</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>ningún ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation>un ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation>%1 ficheiros</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Seleccione unha icona</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Imaxes (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Aplicar os cambios</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Quere aplicar recursivamente estes cambios a todos os ficheiros e subcartafoles?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Debe engadir a lo menos un directorio onde buscar.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Seleccione un cartafol</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Non é posíbel crear ligazóns en sistemas de ficheiros non nativos</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Crear &amp;novo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Pegar</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Seleccionar &amp;todo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Inverter a selección</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Ordenación</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Amosar agachados</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Pr&amp;opiedades do cartafol</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Saída</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Polo nome do ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Pola data de modificación</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Polo tamaño do ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Polo tipo do ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Polo propietario do ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Ascendente</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Descendente</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Primeiro os cartafoles</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Distinguindo maiúsculas de minúsculas</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Nome</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Tipo</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Tamaño</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Modificado</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Propietario</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Grupo</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Negra</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Cursiva</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Conectar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Editar a ruta</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Copiar a ruta</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Lugares</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Escritorio</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Lixo</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Computador</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Aplicativos</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Rede</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Dispositivos</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Marcadores</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Baleirar o lixo</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Abrir nunha lapela nova</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Abrir nunha xanela nova</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Agachar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Mover o marcador cara arriba</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Mover o marcador cara abaixo</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Renomear o marcador</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Retirar o marcador</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Desmontar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Montar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Expulsar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Amosar todas as entradas</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Tipo: %1
+Tamaño: %2
+Modificado: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Tipo: %1
+Modificado: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>&amp;Sobrescribir</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Renomear</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Lugares</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Árbore de directorios</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Non é posíbel restaurar o ficheiro «%s» dende o lixo: descoñecese a ruta orixinal</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Montar</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Conectar &amp;anonimamente</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Conectar como u&amp;suario:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>Nome de &amp;usuario:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Contrasinal:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Dominio:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Esquecer o contrasinal &amp;inmediatamente</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Lembrar o contrasinal ata &amp;pechar a sesión</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>&amp;Lembrar para sempre</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Renomear o ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Introduza un nome novo:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Crear un cartafol</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Introduza un novo nome de ficheiro:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Novo ficheiro de texto</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Introduza un nome novo para o cartafol:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Novo cartafol</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Introduza un nome para o novo %1:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Crear un ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Erro da icona personalizada</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>A ruta non está montada.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Ficheiro de entrada de escritorio non valido: «%1»</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Non foi estabelecido ningún aplicativo predeterminado para iniciar «%1»</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Non é posíbel cambiar o directorio de traballo a «%1»: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation>Identificador: </translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Confirmar a substitución de ficheiros</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Xa existe un ficheiro con este mesmo nome neste lugar.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Quere substituír o ficheiro existente?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>destino</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>co seguinte ficheiro?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>Información do ficheiro «orixe»</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>Información do ficheiro «destino»</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>orixe</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>Nome do &amp;ficheiro:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Aplicar esta opción a todos os ficheiros existentes</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Buscar ficheiros</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Nome/Localización</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Patróns de nomes de ficheiro:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Non distinguir maiúsculas e minúsculas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Usar expresións regulares</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Lugares onde buscar:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Engadir</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Retirar</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Buscar en subdirectorios</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Buscar ficheiros agachados</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Tipo de ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Buscar só os seguintes tipos de ficheiro:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Ficheiros de texto</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Ficheiros de imaxe</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Ficheiros de son</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Ficheiros de vídeo</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Documentos</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Cartafoles</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Contido</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>O ficheiro contén:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>&amp;Non distinguir maiúsculas e minúsculas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>&amp;Usar expresións regulares</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Propiedades</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Tamaño do ficheiro:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Maior que:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Bytes</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Menor que:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Data  da última modificación:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Anterior a:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Posterior a:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_he.ts b/src/translations/libfm-qt_he.ts
new file mode 100644 (file)
index 0000000..ffc5e84
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="he">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>בחירת יישום</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>יישומים מותקנים</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>פקודה בהתאמה אישית</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>שורת פקודה להפעלה:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>שם יישום:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;ניתן להשתמש בקודים המיוחדים האלה בשורת הפקודה:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: מייצג שם של קובץ אחד&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: מייצג שמות של מספר קבצים&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: מייצג כתובת מדויקת של הקובץ&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: מייצג מספר כתובות&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>להשאיר את חלון המסוף פתוח לאחר הפעלת פקודה</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>הפעלה במדמה מסוף</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>הגדרת היישום הנבחר כפעולת בררת המחדל לסוג קובץ זה</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>עריכת סימניות</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>שם</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>מיקום</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>הוספת &amp;פריט</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>ה&amp;סרת פריט</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>ניתן להשתמש בגרירה והשלכה כדי לסדר את הפריטים מחדש</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>הפעלת קובץ</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;פתיחה</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>הפ&amp;עלה</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>הפעלה ב&amp;מסוף</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>ביטול</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>מיקום:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>שם קובץ:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>סוג קובץ:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>יעד:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>עיבוד:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>בהכנה…</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>תהליך</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>הזמן שנותר:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>קבצים שעברו עיבוד:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>מאפייני קובץ</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>כללי</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>מיקום:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>סוג קובץ:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>סוג MIME:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>גודל קובץ:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>גודל בכונן:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>שינוי אחרון:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>יעד קישור:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>פתיחה עם:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>גישה אחרונה:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>הרשאות</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>בעלות</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>קבוצה:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>בעלים:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>בקרת גישה</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>אחר:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>להפוך את הקובץ למורשה הרצה</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>קריאה</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>כתיבה</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>הרצה</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>דביק</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>מצב מתקדם</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>התאמה אישית</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>נא לבחור יישום לפתיחת קובצי „%1”</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>תיקייה</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>קובץ ריק</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>התיקייה שצוינה ‚%1’ אינה תקנית</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>בטעינה…</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;אין תת תיקיות&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>פתיחה ב&amp;לשונית חדשה</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>פתיחה ב&amp;חלון חדש</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>פתיחה במ&amp;סוף</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>להעתיק לכאן</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>להעביר לכאן</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>יצירת קישור סמלי כאן</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>ביטול</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>סימנייה חדשה</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>נראה כי הקובץ ‚%1’ הוא למעשה רשומת שולחן עבודה.
+מה ברצונך לעשות אתו?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>נראה כי קובץ הטקסט ‚%1’ הוא למעשה סקריפט שניתן להריץ.
+מה ברצונך לעשות אתו?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>ניתן להריץ את הקובץ ‚%1’. להריץ אותו?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>חזרה</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+שמאלה</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>קדימה</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+ימינה</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>רענון</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>יצירת תיקייה</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>תצוגת סמלים</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>תצוגת תמונות ממוזערות</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>תצוגה חסכונית</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>תצוגת פירוט</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>שגיאה</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>נא לבחור בקובץ</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 כבר קיים.
+להחליף אותו?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>הנתיב „%1” לא קיים</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>„%1” אינה תיקייה</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>„%1” אינו קובץ</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;פתיחה</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;שמירה</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>כל הקבצים (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>פתיחת קובץ</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>שמירת קובץ</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>פתיחה</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>פתיחה עם…</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>יישומים אחרים</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>יצירת &amp;חדש</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;שחזור</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>גזירה</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>העתקה</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>הדבקה</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>העברה ל&amp;אשפה</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>שינוי שם</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>חילוץ אל…</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>חילוץ לכאן</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>דחיסה</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>מאפיינים</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>מתן אמון בקובצי הפעלה נבחרים</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>מתן אמון בקובץ הפעלה זה</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>פלט</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>מחי&amp;קה</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>שגיאה</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>העברתם של חלק מהקבצים לאשפה אינה אפשרית כיוון שמערכת ההפעלה שתחתיהם אינה תומכת בפעולה זו.
+למחוק אותם במקום?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>אישור</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>למחוק את הקבצים הנבחרים?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>להעביר את הקבצים הנבחרים לסל האשפה?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>העברת קבצים</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>הקבצים הבאים מועברים לתיקיית היעד:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>העתקת קבצים</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>הקבצים הבאים מועתקים לתיקיית היעד:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>השלכת קבצים לאשפה</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>הקבצים הבאים מועברים לסל האשפה:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>מחיקת קבצים</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>הקבצים הבאים נמחקים:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>יצירת קישורים סמליים</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>נוצרים קישורים סמליים לקבצים הבאים:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>שינוי מאפיינים</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>המאפיינים של הקבצים הבאים משתנים:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>שחזור קבצים מהאשפה</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>הקבצים הבאים משוחזרים מסל האשפה:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>שגיאה</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>צפייה בתוכן התיקייה</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>צפייה ועריכה של תוכן התיקייה</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>קריאה</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>קריאה וכתיבה</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>אין הרשאה</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>קבצים מסוגים שונים</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>מגוון קבצים</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>נא לבחור סמל</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>תמונות ‎(*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>החלת השינויים</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>להחיל את השינויים האלה רקורסיבית על כל הקבצים ותת־התיקיות?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>שגיאה</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>עליך להוסיף לפחות תיקייה אחת לחיפוש.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>נא לבחור תיקייה</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>לא ניתן ליצור קישור על מערכת קישור בלתי טבעית</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>יצירת &amp;חדשה</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>ה&amp;דבקה</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>בחירת ה&amp;כול</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>היפוך הבחירה</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>סידור</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>הצגת מוסתרים</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>מאפייני &amp;תיקייה</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>פלט</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>לפי שם קובץ</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>לפי מועד שינוי</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>לפי גודל קובץ</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>לפי סוג קובץ</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>לפי בעלות על קובץ</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>עולה</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>יורד</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>תיקיות בהתחלה</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>תלוי רישיות</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>שם</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>סוג</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>גודל</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>מועד שינוי</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>בעלות</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>קבוצה</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>מודגש</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>נטוי</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>הת&amp;חברות</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>ע&amp;ריכת נתיב</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>ה&amp;עתקת נתיב</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>מיקומים</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>שולחן עבודה</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>מחשב</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>יישומים</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>רשת</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>התקנים</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>סימניות</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>אשפה</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>פתיחה בלשונית חדשה</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>פתיחה בחלון חדש</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>פינוי האשפה</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>הסתרה</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>העברת הסימנייה למעלה</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>העברת הסימנייה למטה</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>שינוי שם לסימנייה</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>הסרת סימנייה</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>ניתוק עיגון</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>עיגון</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>שליפה</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>הצגת כל הרשומות</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>סוג: %1
+גודל: %2
+שינוי: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>סוג: %1
+שינוי: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>&amp;שכתוב</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>שי&amp;נוי שם</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>מיקומים</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>עץ תיקיות</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>לא ניתן להוציא מהאשפה את ‚%s’: הנתיב המקורי אינו מוכר</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>עיגון</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>התחברות &amp;אלמונית</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>התחברות בתור מ&amp;שתמש:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>שם &amp;משתמש:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;ססמה:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>שם מת&amp;חם:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>לש&amp;כוח את הססמה מיידית</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>לשמו&amp;ר את הססמה עד ליציאה</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>ל&amp;זכור לתמיד</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>שינוי שם קובץ</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>נא להקליד שם חדש:</translation>
+    </message>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>שגיאה</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>יצירת תיקייה</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>יצירת קובץ</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>נא להקליד שם חדש לקובץ:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>קובץ טקסט חדש</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>נא להקליד שם חדש לתיקייה:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>תיקייה חדשה</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>שגיאת סמל בהתאמה אישית</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>הנתיב אינו מעוגן.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>קובץ רשומת שולחן עבודה שגוי: ‚%1’</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>לא מוגדר יישום בררת מחדל להפעלת ‚%1’</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>לא ניתן להגדיר את תיקייה העבודה אל ‚%1’:‏ %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>אישור להחלפת הקבצים</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;כבר קיים קובץ בשם הזה במיקום הזה.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;להחליף את הקובץ הקיים?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>יעד</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>בקובץ הבא?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>פרטי קובץ מקור</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>פרטי קובץ יעד</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>מקור</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>שם &amp;קובץ:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>יש להחיל אפשרות זו לכל הקבצים הקיימים</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>חיפוש קבצים</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>שם/מיקום</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>תבניות שמות קבצים:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>ללא תלות ברישיות</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>שימוש בביטויים רגולריים</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>מיקומים לחיפוש:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>הו&amp;ספה</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>הס&amp;רה</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>חיפוש בתת־תיקיות</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>חיפוש אחר קבצים מוסתרים</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>סוג קובץ</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>לחפש קבצים מהסוגים הבאים בלבד:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>קובצי טקסט</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>קובצי תמונות</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>קובצי שמע</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>קובצי וידאו</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>מסמכים</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>תיקיות</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>תוכן</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>הקובץ מכיל:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>ללא תלות ברי&amp;שיות</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>שימוש ב&amp;ביטויים רגולריים</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>מאפיינים</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>גודל קובץ:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>גדול מ־:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>בתים</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>קטן מ־:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>מועד השינוי האחרון:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>לפני:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>אחרי:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_hu.ts b/src/translations/libfm-qt_hu.ts
new file mode 100644 (file)
index 0000000..aaff134
--- /dev/null
@@ -0,0 +1,1559 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="hu">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Alkalmazás választás</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Telepített alkalmazások</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Egyéb parancs</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Végrehajtandó parancs:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Alkalmazás neve:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;A parancssorban használható speciális karakterek:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Egy fájnév&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Több fájlnév&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Egy URI&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Több URI&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Végrehajtás után a terminál nyitva marad</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Végrehajtás külső terminálban</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>A választott alkalmazás rendelődjék a fáljtípushoz</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Könyvjelzők szerkesztése</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Név</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Hely</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>Ho&amp;zzáadás</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>Tö&amp;rlés</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Húzással rendezhető minden elem</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Fájl futtatás</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Nyitás</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>&amp;Futtatás</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Futtatás &amp;terminálban</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Mégse</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation type="unfinished">Hely:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation type="unfinished">Fájltípus:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Cél:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Feldolgozva:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Előkészület...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Folyamat</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Hátralévő idő:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Feldolgozott fájlok:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Fájljellemzők</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Általános</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Hely:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Fájltípus:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Mime típus:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Fájlméret:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Mérete a lemezen:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Módosítási idő:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Link cél:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Megnyitás ezzel:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Utolsó hozzáférés:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Jogosultságok</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Tulajdonos</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Csoport:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Tulajdonos:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Hozzáférés</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Mások:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Futtatható</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Olvas</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Ír</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Végrehajt</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Sticky</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>UID beállítás</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>GID beállítás</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Haladó mód</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Saját beállítás</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Alkalmazás választás a &quot;%1&quot; fájl megnyitásához</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Mappa</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Üres fájl</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>A &apos;%1&apos; mappa érvenytelen</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Olvasás...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Nincs almappa&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Megnyi&amp;tás új lapon</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Me&amp;gnyitás új ablakban</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Megnyitás terminá&amp;lban</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Másolás ide</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Mozgatás ide</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Szimlink ide</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Mégse</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Új könyvjelző</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>A &apos;%1&apos; állomány egy asztalbeállító fájlnak tűnik.
+Mit szeretne tenni vele?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Ez a &apos;%1&apos; fájl futtatható szkriptnek tűnik.
+Mi legyen vele?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Ez a &apos;%1&apos; fájl futtatható. Futtassuk?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation type="unfinished">Mappakészítés</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation type="unfinished">Hiba</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation type="unfinished">&amp;Nyitás</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Nyit</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>&amp;Új létrehozása</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Visszalép</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Kivág</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Másol</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Beilleszt</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>Kukába &amp;mozgat</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Kimenet</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Töröl</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Átnevez</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Megnyitás ezzel...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Más alkalmazások</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Kibontás...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Kibontás ide</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Csomagolás</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Tulajdonságok</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Hiba</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Egyes fájlok nem mozgathatók a kukába, mert a rendszer ezt nem engedélyezi.
+Töröljük őket véglegesen?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Jóváhagyás</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Töröljük a választott fájlokat?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Helyezzük a kukába a választott fájlokat?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Fájlmozgatás</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>A következő fájlok mozgatása ide:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Fájlmásolás</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>A következő fájlok másolása ide:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Fájlok a kukába</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>A következő fájlok kukába mozgatása:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Fájltörlés</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>A következő fájlok törlése:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Szimlink létrehozás</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Szimlink készítés a következő fájlokra:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Attribútum változtatás</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>A következő fájlok attribútum változtatása:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Kukázott fájlok visszaállítása</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>A következő fájlok visszaállítása a kukából:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Hiba</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Mappatartalom</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Mappatartalom és változtatása</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Olvasás</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Olvasás és írás</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Tiltott</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Különféle fájltípusok</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Többszörös fájlok</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Ikon választás</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Képek (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Változtatások alkalmazása</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Minden mappára és fájlra alkalmazzuk a változtatásokat?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Hiba</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Kereséshez legalább egy mappa megadandó.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Mappa választás</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>&amp;Új létrehozása</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Beilleszt</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Minden kivál&amp;asztása</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Kiválasztás fordítása</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Rendezés</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Rejtettek is</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Mappatulajd&amp;onságok</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Kimenet</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Név</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Módosítási idő</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Méret</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Típus</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Tulajdonos</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Emelkedő</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Csökkenő</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Mappák elől</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Nagybetűérzékeny</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Név</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Típus</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Méret</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Módosítva</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Tulaj</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Csoport</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Kövér</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Dőlt</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Kapcsolódás</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>Útvonal sz&amp;erkesztés</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>Útvonal &amp;másolása</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Helyek</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Asztal</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Kuka</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Számítógép</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Alkalmazások</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Hálózat</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Eszközök</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Könyvjelzők</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Kukaürítés</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Megnyitás új lapon</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Megnyitás új ablakban</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Elrejtés</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Könyvjelző föl</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Könyvjelző le</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Könyvjelző átnevezés</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Könyvjelző törlés</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Lecsatol</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Csatol</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Kidobat</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Minden bejegyzés mutatása</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Típus: %1
+Méret: %2
+Módosítva: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Típus: %1
+Módosítva: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>&amp;Felülír</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Átnevez</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Helyek</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Könyvtárfa</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Csatlakozás</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Névtelen cs&amp;atlakozás</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Felha&amp;sználóként:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>&amp;Név:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Jelszó:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Hálózati cím:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Jelszófelejtés &amp;iziben</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Je&amp;lszófelejtés kilépéskor</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Jelszóme&amp;gjegyzés örökre</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Hiba</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Fájlátnevezés</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Új név:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Mappakészítés</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Új fájlnév:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Új szövegfájl</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Űj mappanév:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Új mappa</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Az új %1 neve:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Fájlkészítés</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Egyéb ikon hiba</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Az elérési út nincs csatolva.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Fájlfelülírás megerősítése</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Itt már van ilyen nevű fájl.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Felülírjuk a meglévőt?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>cél</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>Ezzel a fájllal?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>forrásfájl infó</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>célfájl infó</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>forrás</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>&amp;Fájlnév:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Az összes fájlra vonatkozzon mindez</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Fájlkeresés</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Név/hely</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Fájlnév szűrő:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Nagybetű érzéketlen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Szabályos kifejezés</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Keresési helyek:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>Hozzá&amp;ad</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>Tö&amp;röl</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Alkönyvtárakban is keres</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Rejtett fájlok is</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Fájltípus</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Csak ilyen típusú fájlokat keres:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Szöveg</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Kép</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Hang</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Videó</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Dokumentum</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Könyvtár</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Tartalom</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Fájl tartalmazza:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Nagybetű ér&amp;zéketlen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>Szabvá&amp;nyos kifejezés</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Tulajdonságok</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Méret:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Nagyobb mint:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Bájt</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>Kb</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>Mb</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>Gb</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Kissebb mint:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Utolsó módosítás ideje:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Korábbi ennél:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Későbbi ennél:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_id.ts b/src/translations/libfm-qt_id.ts
new file mode 100644 (file)
index 0000000..7b3054e
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="id">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Pilih Aplikasi</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Aplikasi Terpasang</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Perintah Kustom</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Baris perintah untuk dieksekusi:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Nama aplikasi:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Kode khusus berikut dapat digunakan dalam baris perintah:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Mewakili satu nama berkas&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Mewakili beberapa nama berkas&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Mewakili satu URI berkas&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Mewakili beberapa URI berkas&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Biarkan jendela terminal terbuka setelah eksekusi perintah</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Eksekusi di emulator terminal</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Atur aplikasi terpilih sebagai tindakan baku dari tipe berkas ini</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Sunting Markah</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Nama</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Lokasi</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Tambah Item</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>&amp;Hapus Item</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Gunakan drag dan drop untuk menyusun ulang item</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Eksekusi berkas</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>E&amp;ksekusi</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Eksekusi di &amp;Terminal</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Batal</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Lokasi:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Nama berkas:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Tipe berkas:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Tujuan:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Memproses:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Mempersiapkan...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Kemajuan</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Sisa waktu:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Berkas diproses:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Properti Berkas</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Umum</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Lokasi:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Tipe berkas:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Tipe MIME:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Ukuran berkas:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Ukuran pada disk:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Terakhir diubah:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Tautan target:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Buka Dengan:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Terakhir diakses:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Hak akses</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Kepemilikan</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Grup:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Pemilik:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Kontrol Akses</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Lainnya:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Buat berkas dapat dieksekusi</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Baca</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Tulis</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Eksekusi</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Sticky</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>SetUID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>SetGID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Mode Lanjutan</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Kustomisasi</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Pilih aplikasi untuk membuka berkas &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Folder</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Berkas Kosong</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>Direktori &apos;%1&apos; yang ditetapkan tidak valid</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Memuat...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Tanpa sub folder&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Buka di T&amp;ab Baru</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Buka di Jen&amp;dela Baru</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Buka di Termina&amp;l</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Salin disini</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Pindah disini</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Buat symlink disini</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Batal</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Markah baru</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Berkas &apos;%1&apos; ini tampaknya entri desktop.
+Apa yang ingin Anda lakukan?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Berkas teks &apos;%1&apos; ini tampaknya sebuah skrip yang dapat dieksekusi.
+Apa yang ingin Anda lakukan dengannya?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Berkas &apos;%1&apos; ini dapat dieksekusi. Apakah Anda ingin mengeksekusinya?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Kembali</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Left</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Maju</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Right</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Muat ulang</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Buat Folder</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Tampilan Ikon</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Tampilan Gambar Kecil</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Tampilan Kompak</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Tampilan Daftar Rinci</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Kesalahan</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Silakan pilih berkas</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 sudah ada.
+Apakah Anda ingin menggantinya?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Jalur &quot;%1&quot; tidak ada</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>&quot;%1&quot; bukan sebuah direktori</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>&quot;%1&quot; bukan sebuah berkas</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Buka</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;Simpan</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Semua Berkas (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Buka Berkas</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Simpan Berkas</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Buka</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Buka Dengan...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Aplikasi Lainnya</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Buat Baru</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>Pulihkan</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Potong</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Salin</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Tempel</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>Pindah ke Tempat Sampah</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Ubah nama</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Ekstrak ke...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Ekstrak Disini</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Kompres</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Properti</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Percayai executable yang dipilih</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Percayai executable ini</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Keluaran</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>Hapus</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Kesalahan</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Beberapa berkas tidak dapat dipindahkan ke tempat sampah karena sistem berkas yang digunakan tidak mendukung operasi ini.
+Apakah Anda ingin menghapusnya saja?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Konfirmasi</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Apakah Anda ingin menghapus berkas yang dipilih?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Apakah Anda ingin memindahkan berkas yang dipilih ke tempat sampah?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Pindahkan berkas</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Memindahkan berkas berikut ke folder tujuan:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Salin Berkas</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Menyalin berkas berikut ke folder tujuan:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Buang Berkas</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Memindahkan berkas berikut ke tempat sampah:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Hapus Berkas</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Menghapus berkas berikut:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Buat Symlink</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Membuat symlink untuk berkas berikut:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Ubah Atribut</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Mengubah atribut berkas berikut:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Pulihkan Berkas yang Dibuang</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Memulihkan berkas berikut ke tempat sampah:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Kesalahan</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Lihat konten folder</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Lihat dan ubah konten folder</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Baca</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Baca dan Tulis</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Terlarang</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Berkas dari berbagai tipe</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Beberapa Berkas</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Pilih ikon</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Gambar (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Terapkan perubahan</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Apakah Anda ingin secara rekursif menerapkan perubahan ini ke semua berkas dan sub-folder?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Kesalahan</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Anda harus menambahkan setidaknya satu direktori untuk dicari.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Pilih folder</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Tidak dapat membuat tautan pada berkas sistem non-native</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Buat &amp;Baru</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>Tempel</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Pilih Semua</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Pilihan Sebaliknya</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Penyortiran</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Tampilkan Tersembunyi</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Pr&amp;operti Folder</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Keluaran</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Berdasarkan Nama Berkas</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Berdasarkan Waktu Modifikasi</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Berdasarkan Ukuran Berkas</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Berdasarkan Tipe Berkas</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Berdasarkan Pemilik Berkas</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Menaik</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Menurun</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Folder yang Pertama</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Sensitif Kata</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Nama</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Tipe</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Ukuran</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Dimodifikasi</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Pemilik</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Grup</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Tebal</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Miring</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>Sambungkan</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>Sunting Jalur</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>Salin Jalur</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Tempat</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Desktop</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Komputer</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Aplikasi</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Jaringan</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Perangkat</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Markah</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Tempat Sampah</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Buka di Tab Baru</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Buka di Jendela Baru</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Kosongkan Tempat Sampah</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Sembunyikan</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Pindahkan Markah Keatas</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Pindahkan Markah Kebawah</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Ubah Nama Markah</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Hapus Markah</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Lepat Kait</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Kaitkan</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Keluarkan</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Tampilkan semua entri</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Tipe: %1
+Ukuran: %2
+Dimodifikasi: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Tipe: %1
+Dimodifikasi: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>Timpa</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>Ubah Nama</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Tempat</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Pohon Direktori</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Tidak dapat mengembalikan berkas &apos;%s&apos;: jalur asal tidak diketahui</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Kait</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Hubungkan secara &amp;anonim</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Hubungkan sebagai pengguna:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>Nama pengguna:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>Kata sandi:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>Ranah:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Lupakan berkas segera</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Ingat kata sandi sampai Anda log keluar</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Ingat selamanya</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Ubah Nama Berkas</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Silakan masukkan nama baru:</translation>
+    </message>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Kesalahan</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Buat Folder</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Buat Berkas</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Silakan masukkan nama berkas baru:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Berkas teks baru</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Silakan masukkan nama folder baru:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Folder baru</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Masukkan nama untuk %1 baru:</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Kesalahan Ikon Kustom</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Jalur ini tidak dimount.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Berkas entri desktop tidak valid: &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Tidak ada aplikasi default yang diatur untuk menjalankan &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Tidak dapat mengatur direktori kerja menjadi &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Konfirmasi untuk mengganti berkas</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Sudah ada berkas dengan nama yang sama di lokasi ini.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Apakah Anda ingin mengganti berkas yang ada?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>tujuan</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>dengan berkas berikut?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>info berkas sumber</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>info berkas tujuan</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>sumber</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>Nama berkas:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation></translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Cari Berkas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Nama/Lokasi</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Pola Nama Berkas:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Tidak sensitif kata</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Gunakan ekspresi reguler</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Tempat untuk Dicari:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>Tambah</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>Hapus</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Cari di sub direktori</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Cari berkas tersembunyi</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Tipe Berkas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Hanya cari berkas dengan jenis berikut:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Berkas teks</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Berkas gambar</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Berkas audio</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Berkas video</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Dokumen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Folder</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Konten</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Isi berkas:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Tidak sensitif kata</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>G&amp;unakan ekspresi reguler</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Properti</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Ukuran Berkas:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Lebih besar dari:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Byte</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Lebih kecil dari:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Waktu Modifikasi Terakhir:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Lebih awal dari:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Lebih lambat dari:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_it.ts b/src/translations/libfm-qt_it.ts
new file mode 100644 (file)
index 0000000..0ee4daa
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="it">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Scegli un&apos;applicazione</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Applicazioni installate</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Comando personalizzato</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Riga di comando da eseguire:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Nome dell&apos;applicazione:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Questi caratteri speciali possono essere usati nella riga di comando:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: indica un nome file singolo&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: indica diversi nomi file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: indica un URI singolo del file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: indica diversi URI&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Non chiudere il terminale dopo l&apos;esecuzione</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Esegui in un emulatore di terminale</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Ricorda come associazione predefinita per questo tipo di file</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Modifica segnalibri</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Nome</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Posizione</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Aggiungi elemento</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>&amp;Rimuovi elemento</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Trascina per riordinare gli elementi</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Esegui file</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Apri</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>E&amp;segui</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Esegui in un &amp;terminale</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Annulla</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Posizione:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Nome del file:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Tipo file:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Destinazione:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Elaborazione:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Preparazione...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Avanzamento</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Tempo rimanente:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>File elaborati:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Proprietà file</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Generale</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Posizione:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Tipo file:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Tipo MIME:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Dimensione file:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Dimensione sul disco:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Ultima modifica:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Destinazione collegamento:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Apri con:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Ultimo accesso:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Permessi</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Proprietà</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Gruppo:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Proprietario:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Controllo degli accessi</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Altri:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Rendi il file eseguibile</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Lettura</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Scrittura</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Esecuzione</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Sticky Bit</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>Setta UID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>Setta GID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Modalità avanzata</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Personalizza</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Seleziona un&apos;applicazione per aprire i file &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Cartella</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>File vuoto</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>La cartella specificata %1 non è valida</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Caricamento in corso...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;nessuna sottocartella&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Apri in una nuova &amp;scheda</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Apri in una nuova &amp;finestra</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Apri in un &amp;terminale</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Copia qui</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Sposta qui</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Crea collegamento qui</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Annulla</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Nuovo segnalibro</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Questo file &apos;%1&apos; sembra un file .desktop.
+Cosa vuoi fare?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Questo file di testo &quot;%1&quot; sembra essere uno script eseguibile.
+Cosa vuoi fare?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Questo file &quot;%1&quot; è eseguibile. Vuoi eseguirlo?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Indietro</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Sinistra</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Avanti</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Destra</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Ricarica</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Crea cartella</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Vista icone</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Vista miniature</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Vista compatta</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Vista dettagliata</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Errore</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Per favore selezionare un file</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 esiste già.
+Sostituirlo?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Il percorso &quot;%1&quot; non esiste</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>&quot;%1&quot; non è una cartella</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>&quot;%1&quot; non  è un file</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Apri</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;Salva</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Tutti file (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Apri file</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Salva file</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Apri</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Taglia</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Copia</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Incolla</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>Cestin&amp;a</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Fidati degli eseguibili selezionati</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Fidati del eseguibile</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Risultato</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Elimina</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Rinomina</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Apri con...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Altre applicazioni</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Crea &amp;nuovo</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Ripristina</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Estrai in...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Estrai qui</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Comprimi</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Proprietà</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Errore</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Alcuni file non possono essere spostati nel cestino perché il file system su cui si trovano non supporta questa operazione.
+Vuoi invece eliminarli?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Conferma</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Vuoi eliminare i file selezionati?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Vuoi spostare nel cestino i file selezionati?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Sposta file</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Spostamento dei file seguenti nella cartella di destinazione:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Copia file</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Copia dei file seguenti nella cartella di destinazione:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Cestina file</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Spostamento dei file seguenti nel cestino:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Elimina file</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Eliminazione dei file seguenti:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Crea collegamenti simbolici</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Creazione collegamenti simbolici per i seguenti file:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Cambia attributi</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Modifica degli attributi per i seguenti file:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Ripristina file cestinati</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Ripristino dei file seguenti dal cestino:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Errore</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Visualizza il contenuto della cartella</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Visualizza e modifica il contenuto della cartella</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Lettura</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Lettura e scrittura</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Vietato</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>File di tipi diversi</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>File multipli</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Seleziona un icona</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Immagini (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Applica modifiche</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Vuoi applicare ricorsivamente queste modifiche a tutti i file e a tutte le sottocartelle?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Errore</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Dovresti aggiungere almeno una cartella in cui cercare.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Seleziona una cartella</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Impossibile creare un collegamento su un filesystem non nativo</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Crea &amp;nuovo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Incolla</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Seleziona t&amp;utto</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Inverti selezione</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Ordinamento</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Mostra nascosti</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Pr&amp;oprietà cartella</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Per nome file</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Per data modifica</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Per dimensione file</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Per tipo file</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Per proprietario file</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Crescente</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Decrescente</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Prima le cartelle</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Distingui MAIUSCOLE/minuscole</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Nome</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Tipo</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Dimensione</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Modificato</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Proprietario</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Gruppo</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Grassetto</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Corsivo</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Connetti</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Modifica percorso</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Copia percorso</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Risorse</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Scrivania</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Cestino</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Computer</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Applicazioni</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Rete</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Dispositivi</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Segnalibri</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Svuota cestino</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Apri in una nuova scheda</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Apri in una nuova finestra</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Nascondi</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Sposta segnalibro in su</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Sposta segnalibro in giù</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Rinomina segnalibro</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Rimuovi segnalibro</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Smonta</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Monta</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Espelli</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Mostra tutte le risorse</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Tipo: %1
+Dimensione: %2
+Ultima modifica: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Tipo: %1
+Ultima modifica: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>S&amp;ovrascrivi</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Rinomina</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Risorse</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Albero delle cartelle</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Impossibile ripristinare il file &apos;%1&apos;: percorso originale sconosciuto</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Monta</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Connetti in modo &amp;anonimo</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Connetti come &amp;utente:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>&amp;Nome utente:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Password:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Dominio:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Dimentica la password &amp;immediatamente</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Ricorda &amp;la password fino al termine sessione</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Ricorda per &amp;sempre</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Errore</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Rinomina file</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Digita un nuovo nome:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Crea cartella</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Digita un nuovo nome del file:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Nuovo file di testo</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Digita un nuovo nome della cartella:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Nuova cartella</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Digita un nome per %1:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Crea file</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Errore per l&apos;icona personalizzata</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Questo percorso non è montato.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>File desktop non valido: &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Nessuna applicazione predefinita per eseguire  &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Impossibile impostare cartella di lavoro a  &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Conferma sostituzione dei file</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;C&apos;è già un file con lo stesso nome in questa posizione.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Vuoi sostituire il file esistente?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>Destinazione</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>con il file seguente?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>info file sorg</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>info file dest</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>sorg</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>Nome &amp;file:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Applica questa opzione a tutti i file esistenti</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Cerca file</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Nome/Posizione</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Modelli di nome dei file:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Non distinguere le maiuscole</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Utilizza un&apos;espressione regolare</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Risorse in cui cercare:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Aggiungi</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Rimuovi</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Cerca nelle sottocartelle</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Cerca i file nascosti</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Tipo di file</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Cerca solo i file dei tipi seguenti:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>File di testo</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>File di immagini</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>File audio</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>File video</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Documenti</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Cartelle</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Contenuto</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Il file contiene:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Non disting&amp;uere le maiuscole</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>&amp;Utilizza un&apos;espressione regolare</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Proprietà</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Dimensione file:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Più grande di:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Byte</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Più piccolo di:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Ultima modifica:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Prima del:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Dopo del:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_ja.ts b/src/translations/libfm-qt_ja.ts
new file mode 100644 (file)
index 0000000..8c04b38
--- /dev/null
@@ -0,0 +1,1559 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ja">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>アプリケーションを選ぶ</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>インストール済アプリケーション</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>カスタムコマンド</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>実行するコマンドライン:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>アプリケーション名:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;コマンドラインには、次の特別なコードを使用することができます:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: 単一のファイル名を表す&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: 複数のファイル名を表す&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: 単一のファイルのURIを表す&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: 複数のURIを表す&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>コマンド実行後も端末のウィンドウを閉じない</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>端末エミュレーター内で実行</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>選択したアプリケーションをこのファイルの種類に関連付ける</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>ブックマークを編集</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>名前</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>場所</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>アイテムを追加(&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>アイテムを削除(&amp;R)</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>アイテムを並べ替えるにはドラッグアンドドロップ</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>ファイルを実行</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>開く(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>実行(&amp;E)</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>端末内で実行(&amp;T)</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>キャンセル</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation type="unfinished">場所:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation type="unfinished">種類:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>送り先:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>処理中:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>準備中...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>進行状況</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>残り時間:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>ファイルのプロパティー</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>一般</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>場所:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>種類:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>MIMEタイプ:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>サイズ:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>ディスク上のサイズ:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>最終更新日時:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>リンク先:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>関連付け:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>最終アクセス日時:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>パーミッション</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>所有権</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>グループ:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>所有者:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>アクセス制限</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>その他:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>ファイルを実行可能にする</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>読取り</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>書込み</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>実行</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>スティッキー</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>UIDを設定</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>GIDを設定</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>高度なモード</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>カスタマイズ</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>ファイル&quot;%1&quot;を開くアプリケーションを選択</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>フォルダー</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>空のファイル</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>指定されたディレクトリ &apos;%1&apos; は妥当ではありません</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>ロード中...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;サブフォルダーなし&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>新しいタブで開く(&amp;a)</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>新しいウィンドウで開く(&amp;d)</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>ターミナルで開く(&amp;l)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>ここへコピー</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>ここへ移動</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>シンボリックリンクを作成</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>キャンセル</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>新規ブックマーク</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>このテキストファイル &apos;%1&apos; は実行可能スクリプトです。
+どうしますか?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>このファイル &apos;%1&apos; は実行可能です。
+実行しますか?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation type="unfinished">フォルダの作成</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation type="unfinished">エラー</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation type="unfinished">開く(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>開く</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>切り取り</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>コピー</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>貼り付け</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>ゴミ箱へ移動(&amp;M)</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>出力</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>削除(&amp;D)</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>名前を変更する</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>アプリケーションで開く...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>その他のアプリケーション</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>新規作成 (&amp;N)</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>復活(&amp;R)</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>展開する...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>ここへ展開する</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>圧縮する</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>プロパティー</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>ファイルシステムのサポートがないため、ゴミ箱へ移動できないファイルがあります。
+かわりにこれらを削除しますか?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>確認</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>選択したファイルを削除しますか?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>選択したファイルをゴミ箱に移しますか?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>ファイルを移動</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>次のファイルを対象のフォルダーへ移動:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>ファイルをコピー</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>次のファイルを対象のフォルダーへコピー:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>ファイルをゴミ箱へ入れる</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>次のファイルをゴミ箱へ入れる:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>ファイルを削除</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>以下のファイルを削除:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>シンボリックリンク作成</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>以下のファイルのシンボリックリンクを作成:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>属性を変更</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>以下のファイルの属性を変更:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>ゴミ箱から戻す</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>以下のファイルをゴミ箱から戻す:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>フォルダーの内容を表示</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>フォルダーの内容を表示・変更</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>読取り</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>読取りおよび書込み</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>禁止</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>異なる種類のファイル</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>複数のファイル</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>アイコンを選択</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>画像 (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>変更を適用</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>変更をすべてのファイルとサブフォルダーにも再帰的に適用しますか?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>検索するディレクトリを追加してください。</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>フォルダーの選択</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>新規作成 (&amp;N)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>ペースト(&amp;P)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>すべてを選択(&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>選択を反転</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>ソート</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>隠しファイルの表示</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>フォルダのプロパティー(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>出力</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>名前</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>更新時刻</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>サイズ</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>種類</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>所有者</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>昇順</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>降順</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>フォルダーを先に</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>大文字小文字を区別</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>名前</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>種類</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>サイズ</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>更新日時</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>所有者</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>太字</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>斜体</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>接続&amp;(C)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>パスの編集(&amp;E)</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>パスのコピー(&amp;C)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>場所</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>デスクトップ</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>ゴミ箱</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>コンピューター</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>アプリケーション</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>ネットワーク</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>デバイス</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>ブックマーク</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>ゴミ箱を空にする</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>新しいタブで開く</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>新しいウィンドウで開く</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>ブックマークを上に移動</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>ブックマークを下に移動</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>ブックマークの名前を変更</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>ブックマークを削除</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>アンマウント</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>マウント</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>取出し</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>種類: %1
+サイズ: %2
+更新日時: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>種類: %1
+更新日時: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>上書き(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>名前を変更(&amp;R)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>場所</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>ディレクトリーツリー</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>マウント</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>匿名で接続(&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>ユーザーとして接続(&amp;U):</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>ユーザー名(&amp;U):</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;パスワード(&amp;P):</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;ドメイン(&amp;D):</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>パスワードを記憶させない(&amp;i)</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>パスワードをログアウトするまで記憶させる(&amp;l)</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>パスワードを恒久的に記憶させる(&amp;f)</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>エラー</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>ファイル名を変更</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>新しい名前を入力してください:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>フォルダの作成</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>新規ファイルの名前を入力してください:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>新規テキストファイル</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>新規フォルダの名前を入力してください:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>新規フォルダー</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>新しい %1 の名前を入力してください:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>ファイル作成</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>カスタムアイコンのエラー</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>ファイルの置換えを確認</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;この場所にはすでに、同じ名前のファイルがあります。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;既存のファイルを置き換えますか?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>送り先</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>以下のファイルと置き換えますか?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>送り先のファイルの情報</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>送り元のファイルの情報</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>ファイル名(&amp;F):</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>既存のすべてのファイルにも適用する</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>ファイルを検索</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>名前/場所</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>ファイル名のパターン:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>大文字と小文字を区別しない</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>正規表現を使用する</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>検索の場所:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>追加(&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>削除(&amp;R)</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>サブディレクトリを検索する</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>隠しファイルを検索する</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>ファイルの種類</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>次の種類のファイルのみを検索:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>テキストファイル</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>画像ファイル</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>音声ファイル</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>動画ファイル</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>文書</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>フォルダ</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>内容</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>ファイルを含む:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>大文字小文字を区別しない(&amp;v)</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>正規表現を使う(&amp;U)</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>プロパティ</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>ファイルのサイズ:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>より大きい:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Bytes</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>より小さい:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>更新日時:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>より早い:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>より遅い:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_lt.ts b/src/translations/libfm-qt_lt.ts
new file mode 100644 (file)
index 0000000..79cf773
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="lt">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Pasirinkite programą</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Įdiegtos programos</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Tinkinta komanda</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Komandų eilutė, kurią vykdyti:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Programos pavadinimas:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Komandų eilutėje gali būti naudojami šie specialūs kodai:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Atvaizduoja vieno failo pavadinimą&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Atvaizduoja kelių failų pavadinimus&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Atvaizduoja vieną failo URI&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Atvaizduoja kelis URI&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Po komandos įvykdymo, palikti terminalo langą atvertą</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Vykdyti terminalo emuliatoriuje</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Nustatyti pasirinktą programą kaip numatytąjį veiksmą šiam failo tipui</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Redaguoti žymeles</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Pavadinimas</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Vieta</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Pridėti elementą</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>Ša&amp;linti elementą</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Vilkite, norėdami pakeisti elementų tvarką</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Vykdyti failą</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Atverti</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>&amp;Vykdyti</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Vykdyti &amp;terminale</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Atsisakyti</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Vieta:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Failo pavadinimas:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Failo tipas:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Paskirties vieta:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Vykdoma:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Ruošiama...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Eiga</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Liko laiko:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Apdorota failų:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Failo savybės</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Bendra</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Vieta:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Failo tipas:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>MIME tipas:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Failo dydis:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Dydis diske:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Paskutinis pakeitimas:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Nuorodos paskirtis:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Atverti naudojant:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Paskutinė prieiga:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation>Turi:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>Įrenginio naudojimas:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Leidimai</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Nuosavybė</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Grupė:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Savininkas:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Prieigos valdymas</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Kiti:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Padaryti failą vykdomuoju</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Skaityti</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Rašyti</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Vykdyti</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Lipnus</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>Nustatyti UID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>Nustatyti GID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Išplėstinė veiksena</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Tinkinti</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Pasirinkite programą, kad atvertumėte failus &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Aplanką</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Tuščią failą</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>Nurodytas katalogas &quot;%1&quot; yra neteisingas</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Įkeliama...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Nėra poaplankių&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Atverti naujoje kort&amp;elėje</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Atverti naujame lan&amp;ge</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Atverti termina&amp;le</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Kopijuoti čia</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Perkelti čia</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Sukurti simbolinę nuorodą čia</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Atsisakyti</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Nauja žymelė</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Atrodo, kad šis failas &quot;%1&quot; yra darbalaukio įrašas.
+Ką norėtumėte su juo daryti?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Atrodo, kad šis tekstinis failas &quot;%1&quot; yra vykdomasis scenarijus.
+Ką norėtumėte su juo daryti?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Šis failas &quot;%1&quot; yra vykdomasis. Ar norite jį vykdyti?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Grįžti</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Kairėn</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Pirmyn</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Dešinėn</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Įkelti iš naujo</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Sukurti aplanką</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Piktogramų rodinys</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Miniatiūrų rodinys</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Glaustas rodinys</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Išsamaus sąrašo rodinys</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Klaida</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Pasirinkite failą</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 jau yra.
+Ar norite jį pakeisti?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Kelio &quot;%1&quot; nėra</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>&quot;%1&quot; nėra katalogas</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>&quot;%1&quot; nėra failas</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Atverti</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>Į&amp;rašyti</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Visi failai (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Atverti failą</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Įrašyti failą</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Atverti</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Atverti naudojant...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Kitas programas</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Sukurti &amp;naują</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Atkurti</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Iškirpti</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Kopijuoti</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Įdėti</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>&amp;Perkelti į šiukšlinę</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Pervadinti</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Išskleisti į...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Išskleisti čia</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Glaudinti</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Savybės</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Pasitikėti pažymėtais vykdomaisiais</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Pasitikėti šiuo vykdomuoju</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Išvestis</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Ištrinti</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Klaida</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Kai kurie failai negali būti perkelti į šiukšlinę, kadangi esama failų sistema nepalaiko šios operacijos.
+Ar norite vietoj to, juos ištrinti?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Patvirtinti</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Ar norite ištrinti pažymėtus failus?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Ar norite perkelti pažymėtus failus į šiukšlinę?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Perkelti failus</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Šie failai perkeliami į paskirties aplanką:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Kopijuoti failus</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Šie failai kopijuojami į paskirties aplanką:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Perkelti failus į šiukšlinę</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Šie failai perkeliami į šiukšlinę:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Ištrinti failus</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Ištrinami šie failai:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Sukurti simbolines nuorodas</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Šiems failams kuriamos simbolinės nuorodos:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Keisti atributus</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Keičiami šių failų atributai:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Atkurti į šiukšlinę perkeltus failus</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Šie failai atkuriami iš šiukšlinės:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Klaida</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Žiūrėti aplanko turinį</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Žiūrėti ir keisti aplanko turinį</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Skaityti</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Skaityti ir rašyti</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Uždrausta</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Įvairių tipų failai</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Keli failai</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>Panaudota %p%</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>Laisva %1 iš %2</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>jokių failų</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation>vienas failas</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation>%1 failų(-ai)</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Pasirinkti piktogramą</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Paveikslai (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Taikyti pakeitimus</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Ar norite rekursyviai taikyti šiuos pakeitimus visiems failams ir poaplankiams?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Klaida</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Norėdami ieškoti, turite pasirinkti bent vieną katalogą.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Pasirinkti aplanką</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Nepavyksta sukurti nuorodą ne savoje failų sistemoje</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Sukurti &amp;naują</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>Į&amp;dėti</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>&amp;Pažymėti viską</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Invertuoti žymėjimą</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Rikiavimas</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Rodyti paslėptus</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Aplanko &amp;savybės</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Išvestis</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Pagal failo pavadinimą</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Pagal pakeitimo laiką</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Pagal failo dydį</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Pagal failo tipą</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Pagal failo savininką</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Didėjančiai</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Mažėjančiai</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Pirmiausia aplankai</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Skirti raidžių dydį</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Pavadinimas</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Tipas</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Dydis</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Pakeista</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Savininkas</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Grupė</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Pusjuodis</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Kursyvas</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Prijungti</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Taisyti kelią</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Kopijuoti kelią</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Vietos</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Darbalaukis</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Kompiuteris</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Programos</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Tinklas</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Įrenginiai</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Žymelės</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Šiukšlinė</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Atverti naujoje kortelėje</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Atverti naujame lange</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Išvalyti šiukšlinę</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Slėpti</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Pakelti žymelę</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Nuleisti žymelę</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Pervadinti žymelę</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Šalinti žymelę</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Atjungti</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Prijungti</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Išstumti</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Rodyti visus įrašus</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Tipas: %1
+Dydis: %2
+Pakeista: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Tipas: %1
+Pakeista: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>Perraš&amp;yti</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>Pe&amp;rvadinti</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Vietos</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Katalogų medis</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Nepavyksta iškelti failą &quot;%s&quot; iš šiukšlinės: pradinis kelias nėra žinomas</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Prijungti</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Prisijungti &amp;anonimiškai</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Prisijungti kaip naudotoja&amp;s:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>Na&amp;udotojo vardas:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>Sla&amp;ptažodis:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Sritis:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Nedels&amp;iant užmiršti slaptažodį</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Prisiminti slaptažodį tol, kol atsijungsi&amp;te</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Prisiminti visam laik&amp;ui</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Pervadinti failą</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Įrašykite naują pavadinimą:</translation>
+    </message>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Klaida</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Sukurti aplanką</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Sukurti failą</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Įrašykite naują failo pavadinimą:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Naujas tekstinis failas</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Įrašykite naują aplanko pavadinimą:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Naujas aplankas</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Įrašykite pavadinimą naujam %1:</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Tinkintos piktogramos klaida</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Kelias nėra prijungtas.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Neteisingas darbalaukio įrašo failas: &quot;%1&quot;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Nėra nustatyta numatytoji programa, skirta paleisti &quot;%1&quot;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Nepavyksta nustatyti darbinį katalogą į &quot;%1&quot;: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation>Identifikatorius: </translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Patvirtinti failų pakeitimą</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Šioje vietoje jau yra failas tokiu pačiu pavadinimu.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Ar norite pakeisti esamą failą?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>paskirtis</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>šiuo failu?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>šaltinio failo informacija</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>paskirties failo informacija</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>šaltinis</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>&amp;Failo pavadinimas:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Taikyti šią parinktį visiems esamiems failams</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Ieškoti failų</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Pavadinimas/Vieta</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Failo pavadinimo šablonai:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Neskirti didžiųjų ir mažųjų raidžių</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Naudoti reguliarųjį reiškinį</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Vietos, kuriose ieškoti:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Pridėti</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>Ša&amp;linti</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Ieškoti pakatalogiuose</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Ieškoti paslėptų failų</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Failo tipas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Ieškoti tik šių tipų failų:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Tekstiniai failai</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Paveikslų failai</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Garso failai</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Vaizdo failai</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Dokumentai</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Aplankai</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Turinys</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Faile yra:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Neski&amp;rti didžiųjų ir mažųjų raidžių</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>Na&amp;udoti reguliarųjį reiškinį</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Savybės</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Failo dydis:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Didesnis nei:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Baitų</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Mažesnis nei:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Paskutinio pakeitimo laikas:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Anksčiau nei:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Vėliau nei:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_nb_NO.ts b/src/translations/libfm-qt_nb_NO.ts
new file mode 100644 (file)
index 0000000..184cad2
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="nb_NO">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Velg et program</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Installerte programmer</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Selvvalgt kommando</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Kommandolinje å kjøre:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Programnavn:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Disse spesielle kodene kan brukes i en kommandolinje:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Representerer et enkelt filnavn&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Representerer flere filnavn&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Representerer en enkelt URI for filen&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Representerer flere URIer&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Hold terminalvinduet åpent etter å ha kjørt kommandoen</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Kjør i terminalemulator</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Bruk valgte program som standardprogram for denne filtypen</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Rediger bokmerker</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Navn</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Sted</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Legg til</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>&amp;Fjern</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Bruk dra og slipp for å forandre rekkefølge</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Kjør fil</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>K&amp;jør</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Kjør i ter&amp;minal</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Avbryt</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Sted:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Filnavn:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Filtype:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Mål:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Behandler:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Forbereder...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Framdrift</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Tid igjen:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Filer behandlet:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Filegenskaper</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Generelt</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Plassering:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Filtype:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>MIME-type:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Filstørrelse:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Størrelse på disken:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Sist endret:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Lenke til:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Åpne med:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Sist brukt:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation>Inneholder:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>Brukt på enheten:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Tillatelser</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Eierskap</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Gruppe:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Eier:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Tilgangskontroll</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Annet:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Gjør filen kjørbar</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Les</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Skriv</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Kjør</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Blivende</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Avansert modus</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Tilpassing</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Velg et program for å åpne &quot;%1&quot;-filer</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Mappe</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Tom fil</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>Den valgte mappa &apos;%1&apos; er ikke gyldig</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Jobber...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Ingen undermapper&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Åpne i ny fane</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Åpne i nytt vindu</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Åpne i terminal</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Kopier hit</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Flytt hit</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Lag symbolsk lenke hit</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Avbryt</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Nytt bokmerke</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Filen &apos;%1&apos; ser ut til å være en skrivebordsfil.
+Hva vil du gjøre med den?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Tekstfilen &apos;%1&apos; ser ut til å være et kjørbart script.
+Hva vil du gjøre med det?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>File &apos;%1&apos; er kjørbar. Hva vil du gjøre med den?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Gå tilbake</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Venstre</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Gå forover</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Høyre</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Last på nytt</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Lag mappe</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Symbolvisning</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Forhåndsvisningsvisning</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Kompaktvisning</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Detaljert listevisning</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Feil</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Velg en fil</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 finnes allerede.
+Vil du bytte den ut?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Stien &quot;%1&quot; finnes ikke</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>&quot;%1&quot; er ikke en mappe</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>&quot;%1&quot; er ikke en fil</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Åpne</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;Lagre</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Alle filer (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Åpne fil</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Lagre fil</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Åpne</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Åpne med...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Andre programmer</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Lag &amp;Ny</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Gjenopprett</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Klipp ut</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Kopier</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Lim inn</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>&amp;Flytt til søppeldunken</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Gi nytt navn</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Pakk ut til...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Pakk ut her</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Pakk sammen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Egenskaper</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Stol på valgte programmer</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Stol på dette programmet</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Utverdi</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Slett</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Feil</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Noen filer kan ikke bli flyttet til søppeldunken fordi det underliggende filsystemet ikke støtter denne operasjonen.
+Vil du slette dem i stedet?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Bekreft</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Vil du slette de valgte filene?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Vil du flytte de valgte filene til søppeldunken?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Flytt filer</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Flytter følgende filer til valgt mappe:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Kopier filer</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Kopierer følgende filer til valgt mappe:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Søppeldunkfiler</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Flytter følgende filer til søppeldunken:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Slett filer</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Sletter følgende filer:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Skap symbolsk lenke</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Skaper symbolske lenker for følgende filer:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Forandr egenskaper</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Forandrer egenskaper for følgende filer:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Gjenskapte kastede filer</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Gjenskaper følgende filer fra søppeldunken:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Feil</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Se mappeinnhold</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Se og forandr mappeinnhold</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Les</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Les og skriv</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Forbudt</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Filer av ulike typer</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Flere filer</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>%p% brukt</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>%1 ledig på %2</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>ingen fil</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation>en fil</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation>%1 filer</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Velg et symbol</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>bilder (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Utfør endringer</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Vil du utføre disse endringene for alle filer og undermapper?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Feil</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Du bør legge til minst en mappe å søke.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Velg en mappe</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Kan ikke lage en lenke på et fremmed filsystem</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Skap &amp;Ny</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Lim inn</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Velg &amp;alt</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Inverterter valg</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Sortering</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Vis skjulte</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Mappe-&amp;egenskaper</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Utverdi</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Etter filnavn</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Etter sist endret</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Etter størrelse</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Etter type</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Etter eier</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Økende</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Minskende</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Mapper først</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Forskjell på STORE og små bokstaver</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Navn</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Størrelse</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Sist endret</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Eier</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Gruppe</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Fet</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Kursiv</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Koble til</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Forandr sti</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Kopier sti</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Steder</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Skrivebord</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Datamaskin</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Programmer</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Nettverk</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Dingser</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Bokmerker</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Søppeldunk</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Åpne i ny fane</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Åpne i nytt vindu</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Tøm søppeldunk</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Skjul</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Flytt bokmerke oppover</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Flytt bokmerke nedover</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Gi nytt navn til bokmerke</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Fjern bokmerke</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Løs ut</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Last inn</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Spytt ut</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Vis alle</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Type: %1
+Størrelse %2
+Sist endret: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Type: %1
+Sist endret: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>&amp;Skriv over</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Gi nytt navn</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Steder</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Filtre</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Kan ikke gjenopprette filen &apos;%s&apos;: original plassering ukjent</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Last inn</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Koble til &amp;anonymt</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Koble til som &amp;bruker:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>&amp;Brukernavn:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Passord:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Domene:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Glem passord &amp;umiddelbart</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Husk passord til du &amp;logger ut</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Husk &amp;alltid</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Gi nytt filnavn</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Skriv inn nytt navn:</translation>
+    </message>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Feil</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Lag mappe</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Lag fil</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Skriv inn et nytt filnavn:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Ny tekstfil</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Skriv inn et nytt mappenavn:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Ny mappe</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Skriv inn et nytt navn for den nye %1:</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Feil med selvvalgt symbol</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Stien er ikke lastet inn.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Ugyldig skrivebordsfil: &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Ingen program er satt til å åpne &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Kan ikke sette arbeidsmappen til &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation>Identifikasjon: </translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Bekreft for å bytte filer</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Det er allerede en fil med samme navn på dette stedet.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Ønsker du å bytte ut den eksisterende filen?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>til</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>med følgende fil?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>kildefil info</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>tilfil info</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>kilde</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>&amp;Filnavn:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Bruk dette valget på alle eksisterende filer</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Søk filer</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Navn/Sted</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Filnavnmønstre:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Ignorer STORE og små bokstaver</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Bruk regular expression</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Steder å søke:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Legg til</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Fjern</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Søk i undermapper</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Søk etter skjulte filer</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Filtype</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Bare søk etter følgende filtyper:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Tekstfiler</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Bildefiler</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Lydfiler</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Videofiler</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Dokumenter</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Mapper</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Innhold</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Fil inneholder:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Ignorerer STORE&amp; og små bokstaver</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>&amp;Bruk regular expression</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Egenskaper</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Filstørrelse:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Større enn:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Mindre enn:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Sist endret:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Før:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Etter:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_nl.ts b/src/translations/libfm-qt_nl.ts
new file mode 100644 (file)
index 0000000..bc9ebaa
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="nl" sourcelanguage="en">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Kies een toepassing</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Geïnstalleerde toepassingen</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Aangepaste opdracht</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Uit te voeren opdrachtregel:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Naam van toepassing:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Deze speciale codes kunt u gebruiken in de opdrachtregel:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Vertegenwoordigt een enkele bestandnaam&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Vertegenwoordigt meerdere bestandnamen&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Vertegenwoordigt een enkele URI van het bestand&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Vertegenwoordigt meerdere URI&apos;s&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Houd terminalvenster open na het uitvoeren van opdrachtregel</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Voer uit in een terminalnabootser</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Stel gekozen toepassing in als standaardactie voor deze bestandsoort</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Bladwijzers bewerken</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Naam</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Plaats</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>Element &amp;toevoegen</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>Element &amp;verwijderen</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Gebruik sleur en pleur om de elementen opnieuw te rangschikken</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Bestand uitvoeren</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>O&amp;penen</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>&amp;Uitvoeren</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>In een &amp;terminal uitvoeren</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Afbreken</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Locatie:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Bestandsnaam:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Bestandssoort:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Doel:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Bezig met:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Voorbereiden...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Voortgang</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Resterende tijd:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Verwerkte bestanden:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Bestandeigenschappen</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Algemeen</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Locatie:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Bestandsoort:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>MIME-type:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Bestandgrootte:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Omvang op schijf:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Laatst gewijzigd:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Koppelingsdoel:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Openen met:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Laatst benaderd:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Rechten</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Eigendom</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Groep:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Eigenaar:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Toegangsbeheer</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Overige:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Bestand uitvoerbaar maken</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Lezen</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Schrijven</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Uitvoeren</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Klevend</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>SetUID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>SetGID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Geavanceerde modus</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Aanpassen</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Kies een toepassing voor het openen van &apos;%1&apos; bestanden</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Map</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Leeg bestand</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>De opgegeven map &apos;%1&apos; is niet geldig</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Laden...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Geen submappen&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Open in nieuw t&amp;abblad</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Open in nieuw &amp;venster</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Open in termina&amp;lvenster</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Hierheen kopiëren</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Hierheen verplaatsen</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Maak hier een symbolische koppeling</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Afbreken</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Nieuwe bladwijzer</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Dit bestand &apos;%1&apos; lijkt een bureaubladsnelkoppeling te zijn.
+Wat wilt u ermee doen?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Het tekstbestand &apos;%1&apos; lijkt een uitvoerbaar script te zijn.
+Wat wilt u ermee doen?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Het bestand &apos;%1&apos; is uitvoerbaar. Wilt u het uitvoeren?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Ga terug</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Links</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Ga vooruit</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Rechts</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Herladen</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Map maken</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Pictogramweergave</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Miniatuurweergave</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Compacte weergave</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Gedetailleerde lijstweergave</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Fout</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Kies a.u.b. een bestand</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 bestaat reeds.
+Wilt u het vervangen?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Pad &apos;%1&apos; bestaat niet</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>&apos;%1&apos; is geen map</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>&apos;%1&apos; is geen bestand</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>O&amp;penen</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;Opslaan</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Alle bestanden (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Bestand openen</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Bestand opslaan</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Openen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>&amp;Nieuw maken</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>Te&amp;rugzetten</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Knippen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Kopiëren</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Plakken</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>Naar de &amp;prullenbak verplaatsen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Gekozen uitvoerbare bestanden vertrouwen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Vertrouw dit uitvoerbare bestand</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Uitvoer</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Wissen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Hernoemen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Openen met...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Andere toepassingen</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Uitpakken naar...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Hier uitpakken</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Comprimeren</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Eigenschappen</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Fout</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Sommige bestanden kunnen niet naar de prullenbak worden verplaatst, omdat de onderliggende bestandssystemen deze bewerking niet ondersteunen.
+Wilt u ze in plaats daarvan blijvend verwijderen?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Bevestigen</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Wilt u de gekozen bestanden verwijderen?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Wilt u de gekozen bestanden verplaatsen naar de prullenbak?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Bestanden verplaatsen</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Verplaats de volgende bestanden naar de bestemmingsmap:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Bestanden kopiëren</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Kopieer de volgende bestanden naar de bestemmingsmap:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Bestanden naar de prullenbak verplaatsen</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Verplaats de volgende bestanden naar de prullenbak:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Bestanden wissen</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Wis de volgende bestanden:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Symbolische koppelingen maken</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Symbolische maken voor de volgende bestanden:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Eigenschappen veranderen</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Eigenschappen van de volgende bestanden veranderen:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Bestanden terugzetten vanuit de prullenbak</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>De volgende bestanden terugzetten vanuit de prullenbak:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Fout</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Toon mapinhoud</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Mapinhoud tonen en wijzigen</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Lezen</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Lezen en schrijven</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Verboden</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Bestanden van verschillende soorten</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Meerdere bestanden</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Kies een pictogram</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Afbeeldingen (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Veranderingen toepassen</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Wilt u de veranderingen toepassen op alle bestanden en onderliggende submappen?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Fout</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>U dient tenminste één te doorzoeken map toe te voegen.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Kies een map</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Kan geen koppeling aanmaken op een niet-eigen bestandssysteem</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>&amp;Nieuw maken</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Plakken</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>&amp;Alle kiezen</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Keuze omdraaien</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Rangschikking</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Toon verborgen</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Map&amp;eigenschappen</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Uitvoer</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Op bestandnaam</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Op wijzigingstijdstip</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Op bestandgrootte</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Op bestandsoort</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Op bestandeigenaar</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Oplopend</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Aflopend</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Map eerst</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Hoofdlettergevoelig</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Naam</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Soort</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Grootte</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Gewijzigd</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Eigenaar</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Groep</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Vet</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Schuin</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Verbinden</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>Pad &amp;bewerken</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>Pad &amp;kopiëren</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Locaties</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Bureaublad</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Prullenbak</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Computer</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Toepassingen</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Netwerk</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Apparaten</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Bladwijzers</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Prullenbak ledigen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Open in nieuw tabblad</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Open in nieuw venster</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Verbergen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Bladwijzer omhoog verplaatsen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Bladwijzer naar beneden verplaatsen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Bladwijzer hernoemen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Bladwijzer verwijderen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Ontkoppelen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Aankoppelen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Uitwerpen</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Toon alle invoervelden</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Soort: %1
+Omvang: %2
+Gewijzigd: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Soort: %1
+Veranderd: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>Over&amp;schrijven</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Hernoemen</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Locaties</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Boomstructuur van de mappen</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Kan bestand &apos;%s&apos; niet uit de prullenbak halen: oorspronkelijk pad onbekend</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Aankoppelen</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>&amp;Anoniem verbinden</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Verbinden als &amp;gebruiker:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>Gebruikers&amp;naam:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Wachtwoord:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Domein:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Wachtwoord &amp;direct vergeten</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Onthoud wachtwoord totdat u zich af&amp;meldt</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Wachtwoord &amp;voor altijd onthouden</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Fout</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Bestand hernoemen</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Voer a.u.b. een nieuwe naam in:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Map maken</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Voer a.u.b. een nieuwe bestandnaam in:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Nieuw tekstbestand</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Voer a.u.b. een nieuwe mapnaam in:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Nieuwe map</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Voer a.u.b. een naam in voor de/het nieuwe %1:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Bestand maken</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Fout inzake aangepast pictogram</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Het pad is niet aangekoppeld.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Ongeldig bureaubladsnelkoppelingsbestand: &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Er is geen standaardtoepassing ingesteld om &apos;%1&apos; te starten</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Kan werkmap niet instellen op &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Vervangen van bestanden bevestigen</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Er is al een bestand met dezelfde naam op deze plek.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Wilt u het bestaande bestand vervangen?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>doel</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>met het volgende bestand?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>Info over het bronbestand</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>Info over het doelbestand</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>bron</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>&amp;Bestandnaam:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Pas deze optie toe op alle bestaande bestanden</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Bestanden zoeken</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Naam/locatie</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Patronen voor bestandnaam:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Hoofdletterongevoelig</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Gebruik reguliere uitdrukking</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Te doorzoeken locaties:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Toevoegen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Verwijderen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Zoek in submappen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Zoek naar verborgen bestanden</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Bestandsoort</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Zoek alleen naar bestanden van de volgende soorten:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Tekstbestanden</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Afbeeldingbestanden</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Geluidbestanden</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Videobestanden</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Documenten</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Mappen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Inhoud</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Bestand bevat:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Hoofdletteronge&amp;voelig</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>Gebruik gewone uitdrukkingen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Eigenschappen</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Bestandgrootte:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Groter dan:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Bytes</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Kleiner dan:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Laatste wijzigingstijdstip:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Vroeger dan:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Later dan:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_pl.ts b/src/translations/libfm-qt_pl.ts
new file mode 100644 (file)
index 0000000..a040196
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="pl">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Wybór programu</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Zainstalowane programy</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Własny wiersz poleceń</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Wiersz poleceń do wykonania:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Nazwa programu:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;W wierszu poleceń można użyć następujących oznaczeń:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: nazwa pierwszego z zaznaczonych plików&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: nazwy wszystkich zaznaczonych plików&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: ścieżka pierwszego z zaznaczonych plików&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: ścieżki wszystkich zaznaczonych plików&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Zachowanie otwartego terminala po wykonaniu polecenia</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Uruchomienie w emulatorze terminala</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Ustaw wybrany program jako domyślną akcję dla tego typu plików</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Edycja zakładek</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Nazwa</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Położenie</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Dodaj</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>&amp;Usuń</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Przeciągnij i upuść aby zmienić kolejność</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Uruchom</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Otwórz</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>&amp;Uruchom</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Uruchom w &amp;terminalu</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Anuluj</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Położenie:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Nazwa pliku:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Rodzaj pliku:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Cel:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Przetwarzanie:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Przygotowanie…</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Postęp</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Pozostały czas:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Przetworzone pliki:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Właściwości pliku</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Ogólne</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Położenie:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Typ pliku:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Typ mime:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Rozmiar pliku:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Rozmiar na dysku:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Ostatnia modyfikacja:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Położenie docelowe:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Otwieranie za pomocą:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Ostatni dostęp:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Uprawnienia</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Właściciel</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Grupa:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Właściciel:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Prawa dostępu</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Inni:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Ustaw prawo wykonywalności</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Odczyt</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Zapis</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Wykonanie</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Sticky</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>SetUID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>SetGID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Zaawansowane</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Dostosuj</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Wybierz program do otwierania plików &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Katalog</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Pusty plik</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>Określony katalog &apos;%1&apos; nie jest prawidłowy</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Wczytywanie…</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Brak katalogów&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Otwórz w nowej k&amp;arcie</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Otwórz w nowym o&amp;knie</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Otwórz w termina&amp;lu</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Kopiuj tutaj</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Przenieś tutaj</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Utwórz dowiązanie symboliczne</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Anuluj</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Nowa zakładka</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Plik &apos;%1&apos; jest wpisem pulpitu.
+Co chcesz z nim zrobić?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Plik tekstowy &apos;%1&apos; wygląda na wykonywalny skrypt.
+Co chcesz zrobić z tym plikiem?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Plik &apos;%1&apos; jest wykonywalny. Czy chcesz go uruchomić?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Wróć</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Strzałka w lewo</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Idź do tyłu</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Strzałka w prawo</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Przeładuj</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Utwórz katalog</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Widok ikon</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Widok miniatur</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Widok kompaktowy</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Widok listy szczegółowej</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Błąd</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Wybierz plik</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 już istnieje.
+Czy chcesz go zamienić?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Ścieżka &quot;%1&quot; nie istnieje</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>&quot;%1&quot; nie jest katalogiem</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>&quot;%1&quot; nie jest plikiem</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Otwórz</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>Zapi&amp;sz</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Wszystkie pliki (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Otwórz plik</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Zapisz plik</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Otwórz</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Utwórz &amp;nowy</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Przywróć</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Wytnij</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Kopiuj</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Wklej</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>Przenieś do &amp;kosza</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Ufaj zaznaczonym plikom wykonywalnym</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Ufaj temu plikowi wykonywalnemu</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Wyjście</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Usuń</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Zmień nazwę</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Otwórz za pomocą…</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Inny program</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Rozpakuj do…</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Rozpakuj tutaj</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Skompresuj</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Właściwości</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Błąd</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Niektóre pliki nie mogą zostać przeniesione do kosza ponieważ system plików nie obsługuje takiej operacji.
+Czy zamiast tego usunąć te pliki?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Potwierdź</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Czy chcesz usunąć wybrane pliki?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Czy chcesz przenieść wybrane pliki do kosza?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Przenoszenie plików</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Przenoszenie następujących plików do katalogu docelowego:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Kopiowanie plików</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Kopiowanie następujących plików do katalogu docelowego:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Przenoszenie plików do kosza</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Przenoszenie następujących plików do kosza:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Usuwanie plików</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Usuwanie następujących plików:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Tworzenie dowiązań symbolicznych</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Tworzenie dowiązań dla następujących plików:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Zmiana uprawnień</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Zmiana uprawnień dla następujących plików:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Przywracanie plików z kosza</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Przywracanie następujących plików z kosza:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Błąd</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Wyświetlanie zawartości</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Wyświetlanie i modyfikowanie zawartości</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Odczyt</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Odczyt i zapis</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Zabronione</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Pliki różnego typu</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Wiele plików</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Wybierz ikonę</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Pliki obrazów (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Zastosuj zmiany</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Czy chcesz rekursywnie zastosować zmiany do wszystkich plików i podkatalogów?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Błąd</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Musisz dodać przynajmniej jeden katalog.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Wybierz katalog</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Nie można utworzyć nawiązania na nienatywnym systemie plików</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Utwórz &amp;nowy</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Wklej</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Z&amp;aznacz wszystko</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Odwróć zaznaczenie</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Sortowanie</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Wyświetlanie ukrytych plików</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Właściwości &amp;katalogu</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Wyjście</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Według nazwy</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Według czasu modyfikacji</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Według rozmiaru</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Według typu</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Wedłuh właściciela pliku</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Rosnąco</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Malejąco</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Najpierw katalogi</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Uwzględnij wielkość liter</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Nazwa</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Typ</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Rozmiar</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Zmodyfikowany</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Właściciel</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Grupa</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Pogrubienie</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Kursywa</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Podłącz</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Edytuj ścieżkę</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Kopiuj ścieżkę</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Położenia</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Pulpit</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Kosz</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Komputer</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Programy</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Sieć</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Urządzenia</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Zakładki</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Opróżnij kosz</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Otwórz w nowej karcie</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Otwórz w nowym oknie</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Ukryj</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Do góry</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>W dół</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Zmień nazwę</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Usuń</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Odmontuj</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Montuj</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Wysuń</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Pokaż wszystkie wpisy</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Typ: %1
+Rozmiar: %2
+Zmodyfikowany: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Typ: %1
+Zmodyfikowany: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>&amp;Nadpisz</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Zmień nazwę</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Położenia</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Drzewo katalogów</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Nie można przywrócić pliku &apos;%s&apos;: nieznana oryginalna ścieżka</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Montowanie</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Połącz &amp;anonimowo</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Połącz jako &amp;użytkownik:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>&amp;Użytkownik:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Hasło:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Domena:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Zapomnij hasło &amp;od razu</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>&amp;Pamiętaj hasło do wylogowania</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Pamiętaj na &amp;zawsze</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Błąd</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Zmiana nazwy</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Podaj nową nazwę:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Utwórz katalog</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Podaj nazwę pliku:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Nowy plik tekstowy</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Podaj nazwę nowego katalogu:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Nowy katalog</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Podaj nazwę dla pliku %1:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Utwórz plik</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Błąd nieprawidłowej ikony</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Ścieżka nie jest zamontowana.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Nieprawidłowy plik wpisu pulpitu: &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Nie ustawiono domyślnej aplikacji do uruchomienia &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Nie można ustawić katalogu roboczego na &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Potwierdź aby nadpisać pliki</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Plik o podanej nazwie już istnieje w katalogu docelowym.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Czy chcesz nadpisać istniejący plik?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>doc.</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>następującym plikiem?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>inf. o pliku źród.</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>inf. o pliku doc.</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>źród.</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>&amp;Nazwa pliku:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Zastosuj do wszystkich plików</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Wyszukiwanie</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Nazwa/Lokacja</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Nazwa pliku:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Nie uwzględniaj wielkości liter</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Użyj wyrażeń regularnych</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Katalogi do przeszukania:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>Dod&amp;aj</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Usuń</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Przeszukuj podkatalogi</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Przeszukuj pliki ukryte</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Typy plików</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Wyszukaj tylko pliki następujących typów:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Pliki tekstowe</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Obrazki</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Pliki audio</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Pliki wideo</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Dokumenty</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Katalogi</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Zawartość</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Plik zawiera:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>&amp;Nie uwzględniaj wielkości liter</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>&amp;Użyj wyrażeń regularnych</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Właściwości</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Rozmiar pliku:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Większy niż:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Bajty</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Mniejszy niż:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>CZas ostatniej modyfikacji:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Wcześniej niż:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Poźniej niż:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_pt.ts b/src/translations/libfm-qt_pt.ts
new file mode 100644 (file)
index 0000000..bb4a1e8
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="pt">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Escolha uma aplicação</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Aplicações instaladas</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Comando personalizado</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Linha de comandos a executar:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Nome da aplicação:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Esses códigos especiais podem ser usados na linha de comando:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Representa um único nome de ficheiro&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Representa vários nomes de ficheiros&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Representa um único URI do ficheiro&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Representa vários URI&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Manter janela de terminal aberta depois da execução do comando</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Executar no emulador de terminal</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Definir a aplicação selecionada como padrão para este tipo de ficheiro</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Editar Favoritos</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Nome</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Localização</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Adicionar item</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>&amp;Remover item</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Usar arraste e solte para organizar itens</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Executar ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Abrir</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>E&amp;xecutar</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Executar no &amp;Terminal</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Cancelar</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Localização:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Nome do ficheiro:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Tipo de ficheiro:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Destino:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Processamento:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Preparação...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Evolução</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Tempo restante:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Ficheiros processados:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Propriedades do ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Geral</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Localização:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Tipo de ficheiro:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Tipo MIME:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Tamanho do ficheiro:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Tamanho no disco:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Última modificação:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Destino do link:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Abrir Com:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Último acesso:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation>Contém:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>Utilização do dispositivo:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Permissões</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Titularidade</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Grupo:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Proprietário:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Controle de acesso</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Outro:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Marcar o ficheiro como executável</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Leitura</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Gravação</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Execução</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Espesso</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>Definir UID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>Definir GID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Modo avançado</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Personalizar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Selecione um aplicativo para abrir &quot;%1&quot; arquivos</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Pasta</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Ficheiro vazio</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>O diretório especificado &apos;%1&apos; é inválido</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Carregando...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Nenhuma subpastas&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Abrir em Nova &amp;Aba</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Abrir em Nova Ja&amp;nela</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Abrir no Termina&amp;l</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Copiar aqui</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Mover aqui</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Criar link simbólico aqui</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Cancelar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Novo favorito</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Parece que &apos;%1&apos; é uma entrada da área de trabalho.
+O que você deseja fazer?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Parece que o arquivo de texto %1 é um script executável.
+O que deseja fazer?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>O arquivo %1 é um executável. Deseja executar o arquivo?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Voltar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Seta esquerda</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Avançar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Seta direita</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Recarregar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Criar Pasta</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Visualização em Ícones</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Visualização em Miniaturas</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Visualização Compacta</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Visualização em Lista Detalhada</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Por favor, selecione um arquivo</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 já existe.
+Deseja substituir?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Caminho &apos;%1&apos; não existe</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>&apos;%1&apos; não é um diretório</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>&apos;%1&apos; não é um arquivo</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Abrir</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;Salvar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Todos os Arquivos (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Abrir Arquivo</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Salvar Arquivo</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Abrir</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Cortar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Copiar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Colar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>&amp;Mover para lixeira</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Confiar em executáveis selecionados</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Confiar neste executável</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Destino</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Excluir</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Renomear</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Abrir Com...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Outras Aplicações</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Criar &amp;Novo</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Restaurar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Extrair para...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Extrair aqui</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Comprimir</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Propriedades</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Alguns arquivos não podem ser movidos para a lixeira porque o sistema de arquivos não suporta esta operação.
+Deseja apagar permanentemente estes arquivos?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Confirmar</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Deseja apagar o(s) arquivo(s) selecionado(s)?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Deseja mover o(s) arquivo(s) selecionado(s) para a lixeira?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Mover arquivos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>A mover os arquivos para a pasta de destino:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Copiar Arquivos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>A copiar os arquivos para a pasta de destino:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Lixeira</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>A mover os arquivos para a lixeira:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Apagar arquivos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>A apagar estes arquivos:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Criar ligações simbólicas</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>A criar ligações simbólicas a estes arquivos:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Alterar Atributos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>A alterar os atributos destes arquivos:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Restaurar arquivos apagados</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Restaurar estes arquivos da lixeira:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Ver conteúdo da pasta</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Ver e modificar conteúdo da pasta</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Leitura</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Leitura e escrita</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Proibido</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Ficheiros de outro tipo</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Vários ficheiros</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>%p% utilizado</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>%1 de %2 livre</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>nenhum ficheiro</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Selecione um ícone</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Imagens (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Aplicar alterações</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Pretende aplicar as alterações a todos os arquivos e subpastas recursivamente?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Deve adicionar pelo menos um diretório para pesquisar.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Selecionar uma pasta</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Não é possível criar um link no sistema de arquivos não nativo</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Criar &amp;Novo(a)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Colar</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Selecionar &amp;Tudo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Inverter Seleção</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Ordenação</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Exibir Oculto</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Pr&amp;opriedades da Pasta</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Destino</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Por Nome do Arquivo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Por Data de Modificação</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Por Tamanho do Arquivo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Por Tipo de Arquivo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Pelo Proprietário do Arquivo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Ascendente</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Descendente</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Pasta Primeiro</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Sensível a Maiúsculas e Minúsculas</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Nome</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Tipo</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Tamanho</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Modificado</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Proprietário</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Grupo</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Negrito</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Itálico</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Conectar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Editar Caminho</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Copiar Caminho</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Locais</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Área de Trabalho</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Lixeira</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Computador</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Aplicações</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Rede</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Dispositivos</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Favoritos</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Esvaziar Lixeira</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Abrir em Nova Aba</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Abrir em Nova Janela</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Ocultar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Mover Favorito Acima</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Mover Favorito Abaixo</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Renomear Favorito</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Remover Favorito</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Desmontar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Montar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Ejetar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Exibir Todas Entradas</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Tipo: %1
+Tamanho: %2
+Modificado: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Tipo: %1
+Modificado: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>&amp;Sobrescrever</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Renomear</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Locais</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Árvore de Diretórios</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Não é possível restaurar arquivo &apos;%s&apos;: caminho original desconhecido</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Montar</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Conectar &amp;anonimamente</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Conectar como u&amp;suário:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>Nome de &amp;usuário:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Senha:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Domínio:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Esquecer senha &amp;imediatamente</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Lembrar senha até você &amp;sair</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Lembrar &amp;sempre</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Renomear Arquivo</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Por favor, insira um novo nome:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Criar Pasta</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Por favor, insira um novo nome do arquivo:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Novo arquivo de texto</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Por favor, insira um novo nome da pasta:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Nova pasta</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Digite um nome para o novo %1:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Criar Arquivo</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Erro no Ícone Personalizado</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>O caminho não está montado.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Arquivo desktop inválido: &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Nenhum aplicativo padrão está configurado para executar &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Não é possível configurar o diretório de trabalho para &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Confirmar para substituir arquivos</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Já existe um arquivo com o mesmo nome nesta localização.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Deseja substituir o arquivo existente?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>destino</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>com o seguinte arquivo?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>informações do arquivo de origem</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>informações do arquivo de destino</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>origem</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>&amp;Nome do arquivo:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Aplicar opção a todos os arquivos existentes</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Pesquisar Arquivos</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Nome/Localização</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Padrões de Nome de Arquivos:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Não sensível a Maiúsculas e Minúsculas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Utilizar expressão regular</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Locais a Pesquisar:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Adicionar</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Remover</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Pesquisar nos subdiretórios</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Pesquisar por arquivos ocultos</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Tipo de Arquivo</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Apenas pesquisar por arquivos de seguintes tipos:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Arquivos de texto</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Arquivos de imagem</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Arquivos de áudio</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Arquivos de vídeo</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Documentos</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Pastas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Conteúdo</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Arquivo contém:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Não sensí&amp;vel a Maiúsculas e Minúsculas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>&amp;Utilizar expressão regular</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Propriedades</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Tamanho do Arquivo:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Maior que:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Bytes</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GiB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Menor que:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Data da Última Modificação:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Anterior a:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Posterior a:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_pt_BR.ts b/src/translations/libfm-qt_pt_BR.ts
new file mode 100644 (file)
index 0000000..aa1d79b
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="pt_BR">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Escolha uma Aplicação</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Aplicações Instaladas</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Comando Personalizado</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Linha de comandos a executar:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Nome da aplicação:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Esses códigos especiais podem ser usados na linha de comando:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Representa um único nome de arquivo&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Representa vários nomes de arquivos&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Representa um único URI do arquivo&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Representa vários URIs&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Manter janela de terminal aberta depois da execução do comando</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Executar no emulador de terminal</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Definir a aplicação selecionada como padrão para este tipo de arquivo</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Editar Favoritos</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Nome</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Localização</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Adicionar item</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>&amp;Remover item</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Usar arrastar e soltar para organizar itens</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Executar arquivo</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Abrir</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>E&amp;xecutar</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Executar no &amp;Terminal</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Cancelar</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Localização:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Nome do arquivo:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Tipo do arquivo:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Destino:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Processamento:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Preparando...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Progresso</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Tempo restante:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Arquivos processados:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Propriedades do Arquivo</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Geral</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Localização:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Tipo de arquivo:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Tipo MIME:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Tamanho do arquivo:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Tamanho no disco:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Última modificação:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Destino do link:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Abrir com:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Último acesso:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation>Contém:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>Uso de disco:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Permissões</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Propriedades</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Grupo:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Proprietário:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Controle de Acesso</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Outro:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Permitir execução do arquivo como um programa</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Ler</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Escrever</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Executar</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Fixar</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>Definir UID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>Definir GID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Modo Avançado</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Customizar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Selecione uma aplicação para abrir arquivos &quot;%1&quot;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Pasta</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Arquivo em branco</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>O diretório especificado &apos;%1&apos; não é válido</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Carregando...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Nenhuma subpasta&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Abrir em Nova &amp;Aba</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Abrir em Nova Ja&amp;nela</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Abrir no Termina&amp;l</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Copiar aqui</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Mover aqui</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Criar link simbólico aqui</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Cancelar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Novo favorito</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>O arquivo &apos;%1&apos; parece ser uma entrada da área de trabalho.
+O que você deseja fazer?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>O arquivo de texto &apos;%1&apos; parece ser um script executável.
+O que você deseja fazer?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Este arquivo &apos;%1&apos; é executável. Você deseja executá-lo?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Voltar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Seta esquerda</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Avançar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Seta direita</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Recarregar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Criar pasta</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Visualização em ícones</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Visualização em miniaturas</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Visualização compacta</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Visualização em lista detalhada</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Por favor, selecione um arquivo</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 já existe.
+Deseja fazer a substituição?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>O destino &quot;%1&quot; não existe</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>&quot;%1&quot; não é um diretório</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>&quot;%1&quot; não é um arquivo</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Abrir</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;Salvar</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Todos os arquivos (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Abrir arquivo</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Salvar arquivo</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Abrir</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Abrir com...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Outras aplicações</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Criar &amp;novo(a)</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Restaurar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Recortar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Copiar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Colar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>&amp;Mover para a lixeira</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Renomear</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Extrair para...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Extrair aqui</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Comprimir</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Propriedades</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Confiar nos executáveis selecionados</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Confiar neste executável</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Destino</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Deletar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Alguns destes arquivos não podem ser movidos para a lixeira porque o sistema de arquivos não suporta esta operação.
+Deseja apagar permanentemente estes arquivos?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Confirmar</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Deseja excluir o(s) arquivo(s) selecionado(s)?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Deseja mover o(s) arquivo(s) selecionado(s) para a lixeira?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Mover arquivos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Movendo os seguintes arquivos para a pasta de destino:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Copiar arquivos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Copiando os seguintes arquivos para a pasta de destino:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Lixeira</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Movendo os seguintes arquivos para a lixeira:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Deletar arquivos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Deletando os seguintes arquivos:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Criar links simbólicos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Criando links simbólicos para os seguintes arquivos:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Alterar atributos</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Alterando atributos dos seguintes arquivos:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Restaurar arquivos da Lixeira</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Restaurando os seguintes arquivos da Lixeira:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Ver o conteúdo da pasta</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Ver e modificar o conteúdo da pasta</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Ler</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Ler e escrever</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Proibido</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Arquivos de diferentes tipos</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Múltiplos arquivos</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>%p% usados</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>% livre, de %2</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>nenhum arquivo</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation>um arquivo</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation>%1 arquivos</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Selecione um ícone</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Imagens (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Aplicar modificações</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Deseja também aplicar as alterações recursivamente a todos os arquivos e subpastas?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Você deve adicionar pelo menos um diretório para pesquisar.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Selecione uma pasta</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Não é possível criar um link em um sistema de arquivos não nativo</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Criar &amp;novo(a)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Colar</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Selecionar &amp;tudo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Inverter seleção</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Classificação</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Mostrar ocultos</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Pr&amp;opriedades da pasta</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Destino</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Por nome do arquivo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Por data de modificação</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Por tamanho do arquivo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Por tipo do arquivo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Por proprietário do arquivo</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Crescente</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Decrescente</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Pastas primeiro</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Diferenciar Maiúsculas/minúsculas</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Nome</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Tipo</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Tamanho</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Modificado</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Proprietário</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Grupo</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Negrito</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Itálico</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Conectar</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Editar caminho</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Copiar caminho</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Locais</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Área de Trabalho</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Computador</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Aplicações</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Rede</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Dispositivos</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Favoritos</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Lixeira</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Abrir em nova aba</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Abrir em nova janela</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Esvaziar Lixeira</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Ocultar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Mover Favorito acima</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Mover Favorito abaixo</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Renomear Favorito</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Remover Favorito</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Desmontar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Montar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Ejetar</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Mostrar todas as entradas</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Tipo: %1
+Tamanho: %2
+Modificado: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Tipo: %1
+Modificado: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>S&amp;obrescrever</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Renomear</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Locais</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Árvore de diretórios</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Não é possível restaurar o arquivo &apos;%s&apos;: destino de origem desconhecido</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Montar</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Conectar &amp;anonimamente</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Conectar como u&amp;suário:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>&amp;Usuário:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Senha:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Domínio:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Esquecer senha &amp;imediatamente</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Lembrar senha até o final da sua &amp;sessão</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Lembrar para s&amp;empre</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Renomear arquivo</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Por favor, insira um novo nome:</translation>
+    </message>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Erro</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Criar pasta</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Criar arquivo</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Por favor, insira um novo nome do arquivo:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Novo arquivo de texto</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Por favor, insira um novo nome da pasta:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Nova pasta</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Insira um nome para o novo %1:</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Erro no Ícone Personalizado</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>O destino não está montado.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Arquivo de entrada da área de trabalho inválido: &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Nenhum aplicativo padrão foi configurado para iniciar &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Não é possível configurar o diretório de trabalho para &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation>Identificação: </translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Confirme para substituir os arquivos</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Já existe um arquivo com o mesmo nome neste local.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Deseja substituir o arquivo existente?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>destino</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>com o seguinte arquivo?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>informações do arquivo de origem</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>informações do arquivo de destino</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>origem</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>&amp;Nome do arquivo:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Aplicar esta opção a todos os arquivos existentes</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Pesquisar por arquivos</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Nome/Localização</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Padrões de nome de arquivos:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Não diferenciar Maiúsculas/minúsculas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Utilizar expressão regular</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Locais a pesquisar:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Adicionar</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Remover</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Pesquisar nos subdiretórios</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Pesquisar por arquivos ocultos</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Tipo de arquivo</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Pesquisar somente os seguintes tipos de arquivos:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Arquivos de texto</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Arquivos de imagem</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Arquivos de áudio</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Arquivos de vídeo</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Documentos</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Pastas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Conteúdo</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Arquivo contém:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>&amp;Não diferenciar Maiúsculas/minúsculas</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>&amp;Utilizar expressão regular</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Propriedades</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Tamanho do arquivo:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Maior que:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Menor que:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Data da última modificação:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Anterior a:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Posterior a:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_ru.ts b/src/translations/libfm-qt_ru.ts
new file mode 100644 (file)
index 0000000..8358d57
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="ru">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Выбрать приложение</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Установленные приложения</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Своя команда</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Выполняемая командная строка:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Имя приложения:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Эти спец. коды могут быть использованы в командной строке:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Означает имя одного файла&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Означает имена нескольких файлов&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Означает один URI файла&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Означает несколько URI файлов&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Не закрывать окно терминала после выполнения команды</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Запустить в эмуляторе терминала</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Использовать выбранное приложение как действие по умолчанию для файлов этого типа</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Изменить закладки</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Имя</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Расположение</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Добавить элемент</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>&amp;Удалить элемент</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Перетаскивайте элементы мышкой, чтобы изменить их порядок</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Выполнить файл</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Открыть</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>В&amp;ыполнить</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Выполнить в &amp;терминале</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Отмена</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Расположение:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Имя файла:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Тип файла:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Назначение:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Обработка:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Подготовка...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Прогресс</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Времени осталось:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>Обработано файлов:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Свойства файла</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Общие</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Расположение:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Тип файла:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Тип Mime:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Размер файла:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Размер на диске:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Последнее изменение:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Цель ссылки:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Открыть с помощью:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Последний доступ:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation>Содержимое:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>Свободно на диске:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Разрешения</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Владение</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Группа:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Владелец:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Контроль доступа</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Остальные:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Сделать файл исполняемым</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Чтение</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Запись</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Выполнение</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Расширенный режим</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Настроить</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Выберите приложение, чтобы открыть &quot;%1&quot; файлов</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Папку</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Пустой файл</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>Указанная директория &apos;%1&apos; некорректна</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Загрузка...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Нет подпапок&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Открыть в новой вкл&amp;адке</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Открыть в новом о&amp;кне</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Открыть в термина&amp;ле</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Копировать сюда</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Переместить сюда</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Создать здесь ссылку</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Отмена</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Новая закладка</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Файл «%1» является .desktop-файлом.
+Что вы хотите с ним сделать?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Этот текстовый файл &apos;%1&apos; похож на исполняемый скрипт.
+Что вы хотите с ним сделать?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Файл &apos;%1&apos; является исполняемым. Вы хотите запустить его?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Назад</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Влево</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>Вперед</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Вправо</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Обновить</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Создать папку</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>В виде значков</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>В виде эскизов</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Компактно</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>В виде подробного списка</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Ошибка</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Выберите файл</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 уже существует.
+Вы хотите заменить его?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>Путь «%1» не существует</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>«%1» не является каталогом</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>«%1» не является файлом</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Открыть</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;Сохранить</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Все файлы (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Открыть файл</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Сохранить файл</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Открыть</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Открыть с помощью...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Другие приложения</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>&amp;Создать</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Восстановить</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Вырезать</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Копировать</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Вставить</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>&amp;Переместить в корзину</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Переименовать</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Распаковать в...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Распаковать здесь</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Сжать</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Свойства</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Доверять выбранным исполняемым файлам</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Доверять этому исполняемому файлу</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Вывод</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Удалить</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Ошибка</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Некоторые файлы не могут быть помещены в корзину, поскольку файловая система не поддерживает эту операцию.
+Вы хотите удалить их вместо перемещения?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Подтвердить</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Вы действительно хотите удалить выбранные файлы?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Вы действительно хотите переместить выбранные файлы в корзину?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Перемещение файлов</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Перемещение следующих файлов в папку назначения:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Копирование файлов</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Копирование следующих файлов в папку назначения:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Перемещение файлов в корзину</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Перемещение следующих файлов в корзину:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Удаление файлов</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Удаление следующих файлов:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Создание ссылок</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Создание ссылок на следующие файлы:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Изменение атрибутов</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Изменение атрибутов следующих файлов:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Восстановление файлов из корзины</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Восстановление следующих файлов из корзины:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Ошибка</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Просмотр содержимого папки</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Просмотр и изменение содержимого папки</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Чтение</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Чтение и запись</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Запрещено</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Файлы разного типа</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Несколько файлов</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>%p% занято</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>%1 свободно из %2</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>нет файлов</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation>1 файл</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished">Файлов: %1</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Выберите значок</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Изображения (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Применить изменения</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Вы хотите применить изменения рекурсивно ко всем файлам и подпапкам?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Ошибка</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Вы должны добавить не менее одного места для поиска.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Выберите папку</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Файловая система не поддерживает создание ссылок</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>&amp;Создать</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Вставить</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Выбрать &amp;всё</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Инвертировать выделение</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Сортировка</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Показать скрытые</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>&amp;Свойства папки</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Вывод</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>По имени</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>По времени изменения</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>По размеру</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>По типу</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>По владельцу</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Возрастающая</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Убывающая</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Сначала папки</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Чувствительность к регистру</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Имя</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Тип</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Размер</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Изменён</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Владелец</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Группа</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Жирный</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Курсив</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Соединить</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Редактировать путь</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Копировать путь</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Места</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Рабочий стол</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Компьютер</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Приложения</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Сеть</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Устройства</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Закладки</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Корзина</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Очистить корзину</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Открыть в новой вкладке</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Открыть в новом окне</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Скрыть</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Сдвинуть закладку вверх</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Сдвинуть закладку вниз</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Переименовать закладку</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Удалить закладку</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Отключить</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Подключить</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Извлечь</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Показать все элементы</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Тип: %1
+Размер: %2
+Изменён: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Тип: %1
+Изменён: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>&amp;Перезаписать</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Переименовать</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Места</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Дерево папок</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>Не удается восстановить файл «%s»: не найден первоначальный путь</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Подключить</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Подключиться &amp;анонимно</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>Подключиться как &amp;пользователь:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>&amp;Имя пользователя:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Пароль:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Домен:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Забыть пароль &amp;сразу</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Запомнить пароль пока вы не &amp;вышли</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Запомнить &amp;навсегда</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Ошибка</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Переименовать файл</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Введите новое имя:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Создать папку</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Введите имя нового файла:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Новый текстовый файл</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Введите имя новой папки:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Новая папка</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Введите имя для нового %1:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Создать файл</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Ошибка пользовательского значка</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Путь не примонтирован.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Неверный файл .desktop: «%1»</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>Не задано приложение по умолчанию для открытия «%1»</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Не удалось задать рабочий каталог «%1»: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished">Устройство: </translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Подтвердить замену файлов</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;В этом месте назначения уже есть файл с таким именем.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Вы хотите заменить существующий файл?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>следующим файлом?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>&amp;Имя файла:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Запомнить выбор для всех уже существующих файлов</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Искать файлы</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Имя/Расположение</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Шаблоны имени файла:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Нечувствительный к регистру поиск</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Использовать регулярные выражения</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Места для поиска:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Добавить</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Удалить</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Искать в подпапках</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Искать скрытые файлы</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Тип файла</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Искать файлы только следующих типов:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Текстовые файлы</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Изображения</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Аудиофайлы</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Видеофайлы</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Документы</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Папки</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Содержание</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Файл содержит:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Нечувствительн&amp;ый к регистру поиск</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>&amp;Использовать регулярные выражения</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Свойства</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Размер файла:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Больше, чем:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Байт</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>КиБ</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>МиБ</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>ГиБ</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Меньше, чем:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Время последнего изменения:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Раньше, чем:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Позже, чем:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_tr.ts b/src/translations/libfm-qt_tr.ts
new file mode 100644 (file)
index 0000000..b5b85d4
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="tr">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Bir Uygulama Seç</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Yüklenmiş Uygulamalar</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Özel Komut</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Çalıştırılacak komut satırı:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Uygulama Adı:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Bu özel kodlar komut satırında kullanılabilir:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Tek dosya adını temsil eder&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Birden fazla dosya adını temsil eder&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Tek dosyanın yolunu temsil eder&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Birden fazla dosyanın yolunu temsil eder&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Komutu çalıştırdıktan sonra uçbirim penceresini açık tut</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Uçbirimde çalıştır</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Seçilen uygulamayı bu dosya türü için öntanımlı olarak ayarla</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Yer İmlerini Düzenle</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>İsim</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Konum</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Öge Ekle</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>Ögeyi &amp;Sil</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Ögeleri yeniden sıralamak için sürükle bırak</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Dosyayı çalıştır</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Aç</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>Ç&amp;alıştır</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Uçbirimde çalış&amp;tır</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>İptal</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>Konum:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>Dosya adı:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>Dosya tipi:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Hedef:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>İşleniyor:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Hazırlanıyor...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>İlerleme</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Kalan Süre:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>İşlenen dosyalar:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Dosya Özellikleri</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Genel</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Konum:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Dosya türü:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Mime Türü:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Dosya boyutu:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Diskteki boyutu:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Son değiştirilme:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Bağlantı hedefi:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Birlikte Aç:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Son erişim:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation>Aygıt Kullanımı:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>İzinler</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Sahiplik</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Grup:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Sahibi:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Erişim Kontrolü</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Diğer:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Dosyayı çalıştırılabilir yap</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Okuyabilir</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Yazabilir</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Çalıştırabilir</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>Yapışkan</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>SetUID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>SetGID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Gelişmiş Kip</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Özelleştir</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>&quot;%1&quot; dosyalarını açmak için bir uygulama seçin</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Dizin</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Boş Dosya</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>Belirtilen dizin &apos;%1&apos; geçerli değil</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Yükleniyor...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Alt dizin yok&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Yeni Sekme&amp;de Aç</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Yeni &amp;Pencerede Aç</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>&amp;Uçbirimde Aç</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Buraya kopyala</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Buraya taşı</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Buraya sembolik bağlantı oluştur</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>İptal</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Yeni yerimi</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>Bu dosya &apos;%1&apos; bir masaüstü dosyası gibi görünüyor.
+Ne yapmak istersiniz?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Bu metin dosyası &apos;%1&apos; çalıştırılabilir bir betik gibi görünüyor.
+Ne yapmak istersiniz?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Bu dosya &apos;%1&apos; çalıştırılabilir. Çalıştırmak ister misiniz?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>Geri</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Sol Ok</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>İleri</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Sağ Ok</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>Yeniden Yükle</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>Dizin Oluştur</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>Simge Görünümü</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>Önizleme</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>Sıkışık Görünüm</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>Detaylı Liste Görünümü</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>Hata</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>Lütfen bir dosya seçin</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 halihazırda var.
+Değiştirmek istiyor musunuz?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>&quot;%1&quot; yolu mevcut değil</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>&quot;%1&quot; bir dizin değil</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>&quot;%1&quot; bir dosya değil</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Aç</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>&amp;Kaydet</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>Tüm Dosyalar (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>Dosyayı Aç</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>Dosyayı Kaydet</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Aç</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Birlikte Aç...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Diğer Uygulamalar</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Ye&amp;ni Oluştur</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Geri Yükle</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Kes</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Kopyala</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Yapıştır</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>&amp;Çöp Kutusuna Taşı</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Yeniden Adlandır</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Buraya çıkart...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Buraya çıkart</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Sıkıştır</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Özellikler</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>Seçilen çalıştırılabilen dosyalara güven</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>Bu çalıştırılabilen dosyaya güven</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Çıktı</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>&amp;Sil</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Hata</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Bazı dosyalar çöp kutusuna taşınamıyorlar.Çünkü dosya sistemi bu işlemi desteklemiyor. 
+Onun yerine silmek ister misiniz?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Doğrula</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Seçilen dosyaları silmek istiyor musunuz?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Seçilen dosyaları çöp kutusuna taşımak istiyor musunuz?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Dosyaları taşı</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Aşağıdaki dosyalar hedef dizine taşınıyor:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Dosyaları Kopyala</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Aşağıdaki dosyalar hedef dizine kopyalanıyor:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Dosyaları Çöpe Gönder</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Aşağıdaki dosyalar çöp kutusuna taşınıyor:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Dosyaları Sil</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Aşağıdaki dosyalar siliniyor:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Sembolik Bağlantı Oluştur</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Aşağıdaki dosyalar için sembolik bağlantılar oluşturuluyor:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Öznitelikleri Değiştir</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Aşağıdaki dosyaların öznitelikleri değişiyor:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Çöp Kutusundaki Dosyaları Geri Yükle</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Aşağıdaki dosyalar çöp kutusundan geri yükleniyor:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Hata</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Dizin içeriğini görebilir</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Dizin içeriğini görebilir ve değiştirebilir</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Okuyabilir</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Okuyabilir ve Yazabilir</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>İzin yok</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Farklı türlerin dosyaları</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Çoklu Dosyalar</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation>kullanılan %p%</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation>%2 &apos;nin %1</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation>dosya yok</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation>bir dosya</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation>%1 dosya</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>Bir simge seçin</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>Resimler (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Değişiklikleri uygula</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Bu değişiklikleri tüm dosya ve alt dizinlere uygulamak istiyor musunuz?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Hata</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Arama yapmak için en az bir dizin eklemelisiniz.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Bir dizin seç</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>Yerel olmayan dosya sisteminde bağlantı oluşturulamıyor</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Ye&amp;ni Oluştur</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Yapıştır</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Tümünü &amp;Seç</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Seçimi Tersine Çevir</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Sıralama</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Gizli Dosyaları Göster</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Dizin Ö&amp;zellikleri</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Çıktı</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>Dosya Adına Göre</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>Değiştirme Tarihine Göre</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>Dosya Boyutuna Göre</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>Dosya Türüne Göre</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>Sahibine Göre</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>Artan</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>Azalan</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Önce Dizin</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Büyük Küçük Harf Duyarlı</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>İsim</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Tür</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Boyut</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Değiştirildi</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Sahip</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>Grup</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Kalın</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Yatık</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;Bağlan</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>Yolu Düz&amp;enle</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>Yolu &amp;Kopyala</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Konumlar</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Masaüstü</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Bilgisayar</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Uygulamalar</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Ağ</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Aygıtlar</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Yer İmleri</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Çöp</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Yeni Sekmede Aç</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Yeni Pencerede Aç</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Çöp Kutusunu Boşalt</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>Gizle</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Yer İmini Yukarı Taşı</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Yer İmini Aşağı Taşı</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Yer İmini Yeniden Adlandır</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Yer İmini Sil</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Ayır</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Bağla</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Çıkart</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>Tüm Girdileri Göster</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Tür: %1
+Boyut: %2
+Değiştirildi: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Tür: %1
+Değiştirild: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>&amp;Üzerine Yaz</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>&amp;Yeniden Adlandır</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Konumlar</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Dizin Ağacı</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>&apos;%s&apos; dosyası çöpten alınamıyor: dosyanın ilk yolu bilinmiyor</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Bağla</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>Anonim ol&amp;arak bağlan</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>K&amp;ullanıcı olarak bağlan:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>K&amp;ullanıcı adı:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Parola:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Alan:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Parolayı der&amp;hal unut</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Parolayı &amp;çıkıncaya kadar hatırla</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Her &amp;zaman hatırla</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Dosyayı Yeniden Adlandır</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Lütfen yeni bir isim girin:</translation>
+    </message>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Hata</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Dizin Oluştur</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Dosya Oluştur</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Lütfen yeni bir dosya adı girin:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Yeni metin dosyası</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Lütfen yeni bir dizin adı girin:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Yeni dizin</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Yeni %1 için bir isim girin:</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>Özel Simge Hatası</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>Yol bağlanamadı.</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>Geçersiz masaüstü dosyası: &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>&apos;%1&apos; çalıştırmak için öntanımlı uygulama yok</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>Çalışma dizini &apos;%1&apos; olarak ayarlanamıyor: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation>Tanımlayıcı: </translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Değiştirilecek dosyaları onaylayın</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Bu konumda aynı isimli bir dosya zaten var.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Var olan dosyayı değiştirmek istiyor musunuz?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>hedef</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>aşağıdaki dosya ile?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>kaynak dosya bilgisi</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>hedef dosya bilgisi</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>kaynak</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>&amp;Dosya adı:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Bu seçeneği tüm var olan dosyalara uygula</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Dosya Ara</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>İsim/Konum</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Dosya Adı Modelleri:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Büyük Küçük Harf Duyarlı</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Düzenli ifade kullan</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Aranacak Yerler:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Ekle</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Sil</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Alt dizinlerde ara</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Gizli dosyalarda ara</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Dosya Türü</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Sadece aşağıdaki dosya türleri için arama yap:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Metin dosyaları</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Resim dosyaları</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Ses dosyaları</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Video dosyaları</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Belgeler</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Dizinler</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>İçerik</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Dosya içeriyor:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Büyük Küçük &amp;Harf Duyarlı</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>Düzenli ifade k&amp;ullan</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Özellikler</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Dosya Boyutu:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Bundan daha büyük:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Bit</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>KBit</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>MB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>GB</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Bundan daha küçük:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Son Değiştirilme Zamanı:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Bundan önce:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Bundan sonra:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_uk.ts b/src/translations/libfm-qt_uk.ts
new file mode 100644 (file)
index 0000000..2046467
--- /dev/null
@@ -0,0 +1,1558 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="uk">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>Вибір програми</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>Встановлені програми</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>Власна команда</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>Командна стрічка для виконання:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>Назва програми:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;Ці спеціальні коди можна використати в командній стрічці:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: вказує назву одного файлу&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: вказує назви багатьох файлів&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: вказує URI на один файл&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: вказує на багато URI&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>Залишати термінал відкритим після виконання команди</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>Виконати в емуляторі терміналу</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>Встановити вибрану програму як типову для цього типу файлу</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>Редагувати закладки</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>Назва</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>Розташування</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>&amp;Додати закладку</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>&amp;Вилучити закладку</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>Використайте захоплення і переміщення для зміни порядку</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>Виконати файл</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>&amp;Відкрити</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>В&amp;иконати</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>Виконати в &amp;терміналі</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>Припинити</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation type="unfinished">Розташування:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation type="unfinished">Тип файлу:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>Призначення:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>Обробка:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>Підготовка...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>Прогрес</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>Залишилося часу:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>Властивості файлу</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>Загальне</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>Розташування:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>Тип файлу:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Тип MIME:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>Розмір файлу:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>Розмір на диску:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>Востаннє змінено:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>Посилання:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>Відкрити з:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>Останній доступ:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>Дозволи</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>Власність</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>Група:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>Власник:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>Контроль доступу</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>Інше:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>Зробити файл виконувальним</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>Читати</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>Записувати</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>Виконувати</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation>До уваги</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation>ВстUID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation>ВстGID</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>Розширений режим</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>Змінити</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>Вибір програми для відкриття  &quot;%1&quot; файлів</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>Тека</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>Порожній файл</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>Завантаження...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;Немає підтек&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>Відкрити в новій в&amp;кладці</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>Відкрити в новому вік&amp;ні</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>Відкрити в терміна&amp;лі</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>Скопіювати тут</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>Перемістити тут</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>Створити посилання тут</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>Перервати</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>Нова закладка</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>Цей текстовий файл &apos;%1&apos; здається є виконувальним скриптом.
+Що Ви бажаєте зробити з ним?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>Цей файл &apos;%1&apos; є виконувальним. Ви бажаєте його виконати?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation type="unfinished">Створити теку</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation type="unfinished">Помилка</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation type="unfinished">&amp;Відкрити</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>Відкрити</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>Відкрити з...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>Інші програми</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>Створити &amp;новий</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>&amp;Відновити</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>Врізати</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>Скопіювати</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>Вставити</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>&amp;Перемістити у смітник</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>Перейменувати</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>Розпакувати до...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>Розпакувати тут</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>Стиснути</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>Властивості</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>Вивід</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>Ви&amp;лучити</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>Помилка</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>Деякі файли неможливо перемістити до смітника, оскільки ця система не підтримує цю операцію.
+Ви бажаєте вилучити їх у цьому випадку?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>Підтвердження</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>Ви бажаєте вилучити вибрані файли?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>Ви бажаєете перемістити вибрані файли до смітника?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>Переміщення файлів</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>Переміщення наступних файлів до теки призначення:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>Копіювання файлів</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>Копіювання файлів до теки призначення:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>Переміщення файлів у смітник</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>Переміщення наступних файлів до смітника:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>Вилучення файлів</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>Вилучення вибраних файлів:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>Створення символьних посилань</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>Створення символьних посилань для настпних файлів:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>Зміна атрибутів</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>Зміна атрибутів наступних файлів:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>Відновлення файлів з смітника</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>Відновлення наступних файлів з смітника:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>Помилка</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>Перегляд вмісту теки</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>Перегляд і зміна вмісту теки</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>Читання</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>Читання і запис</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>Заборонено</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>Файли різних типів</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>Багато файлів</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>Застосувати зміни</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>Ви бажаєте рекурсивно застосувати ці зміни до всіх файлів і підтек? </translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>Помилка</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>Ви повинні додати хоча б одну теку для пошуку.</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>Вибір теки</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>Створити &amp;новий</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>&amp;Вставити</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>Вибрати в&amp;се</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>Обернути зазначення</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>Сортування</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>Показати приховане</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>Властив&amp;ості теки</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>Вивід</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>По назві файлу</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>По даті зміни</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>По розміру файлу</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>По типу файлу</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>По власнику файлу</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>В порядку зростання</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>В порядку спадання</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>Теки спочатку</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>Чутливі до регістру</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>Назва</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>Тип</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>Розмір</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>Змінено</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>Власник</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>Жирний</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>Курсив</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>&amp;З&apos;єднання</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>&amp;Шлях редагування</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>&amp;Шлях копіювання</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Місця</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>Стільниця</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>Комп&apos;ютер</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>Програми</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>Сітка</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>Пристрої</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>Закладки</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>Смітник</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>Відкрити в новій вкладці</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>Відкрити в новому вікні</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>Спорожнити смітник</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>Перенести закладку вгору</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>Перенести закладку вниз</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>Перейменувати закладку</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>Вилучити закладку</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>Відмонтувати</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>Змонтувати</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>Витягнути</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>Тип: %1
+Розмір: %2
+Змінено: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>Тип: %1
+Змінено: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>Пе&amp;резаписати</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>Пер&amp;ейменувати</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>Місця</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>Дерево тек</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>Монтування</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>З&apos;єднати &amp;анонімно</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>З&apos;єднати як к&amp;ористувач:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>&amp;Користувач:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>&amp;Пароль:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>&amp;Домен:</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>Забути пароль &amp;відразу</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>Пам&apos;ятати пароль до ви&amp;ходу</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>Запам&apos;ятати назав&amp;жди</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>Перейменувати файл</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>Будь ласка, введіть нову назву:</translation>
+    </message>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>Помилка</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>Створити теку</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>Створити файл</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>Будь ласка, введіть назву нового файлу:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>Новий текстовий файл</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>Будь ласка, введіть нову назву теки:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>Нова тека</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>Введіть назву для нової %1:</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>Підтвердіть заміщення файлів</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Ту вже є файл з такою самою назвою в тому місці.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Ви бажаєте замістити існуючий файл?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>призн</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>наступним файлом?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>інф про джер файл</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>інф про файл призн</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>джер</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>&amp;Назва файлу:</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>Застосувати цей параметр до всіх існуючих файлів</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>Пошук файлів</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>Назва/Місце</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>Шаблони назв файлів:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation>*</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>Чутливість до регістру</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>Використовувати регулярні фрази</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>Місця для пошуку:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Додати</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Вилучити</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>Пошук в підтеках</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>Пошук прихованих файлів</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>Тип файлу</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>Пошук файлів тільки наступних типів:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>Текстові файли</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>Файли малюнків</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>Аудіо файли</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>Відео файли</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>Документи</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>Теки</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>Вміст</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>Файл містить:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>Нечутливість до ре&amp;гістру</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>&amp;Використовувати регулярні вирази</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>Властивості</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>Розмір файлу:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>Більший ніж:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>Байти</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation>КіБ</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation>МіБ</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation>ГіБ</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>Менший ніж:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>Час останньої зміни:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>Раніше ніж:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>Пізніше ніж:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_zh_CN.ts b/src/translations/libfm-qt_zh_CN.ts
new file mode 100644 (file)
index 0000000..a5b26fe
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_CN">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>选择应用程序</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>已安装的应用程序</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>自定义命令</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>要执行的命令行:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>应用程序名称:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;这些特殊符号可以在命令行参数中使用:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;:代表单个文件名称&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;:代表多个文件名称&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;:代表单一文件 URI&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;:代表多个 URI&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>执行命令后保持终端窗口开启</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>在终端模拟器中执行</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>将所选应用程序设置为这种文件类型的默认程序</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>编辑书签</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>名称</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>位置</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>新增项目(&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>移除项目(&amp;R)</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>使用拖放重新排序项目</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>执行文件</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>打开(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>执行(&amp;X)</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>在终端内执行(&amp;T)</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>取消</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>位置:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>文件名:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>文件类型:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>目标:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>正在处理:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>正在准备...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>进度</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>剩余时间:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>已处理文件:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>文件属性</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>常规</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>位置:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>文件类型:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>MIME 类型:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>文件大小:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>占用空间:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>修改时间:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>链接目标:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>打开方式:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>访问时间:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>权限</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>所有权</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>组:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>所有者:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>访问限制</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>其他:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>使文件可执行</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>只读</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>写入</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>执行</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>高级模式</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>自定义</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>选择用于打开“%1”文件的应用程序</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>文件夹</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>空文件</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>指定的目录“%1”无效</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>正在加载……</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;没有子文件夹&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>在新标签页中打开(&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>在新窗口中打开(&amp;D)</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>在终端中打开(&amp;I)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>复制至此</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>移动至此</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>在这里创建符号链接</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>取消</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>新建书签</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>此文件“%1”像是桌面快捷方式。
+如何处理?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>此文本文件“%1”像是可执行脚本。
+如何处理?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>文件“%1”可以执行,是否执行?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>返回</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+左方向键</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>前进</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+右方向键</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>刷新</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>新建文件夹</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>图标视图</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>缩略图视图</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>列表视图</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>详细信息视图</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>错误</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>请选择文件</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 已存在。
+是否替换?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>路径“%1”不存在</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>“%1”不是目录</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>“%1”不是文件</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>打开(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>保存(&amp;S)</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>所有文件 (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>打开</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>打开方式...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>其他应用程序</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>新建(&amp;N)</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>恢复(&amp;R)</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>剪切</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>复制</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>粘贴</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>移至回收站(&amp;M)</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>重命名</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>解压到...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>解压至此</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>压缩</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>属性</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>输出</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>删除(&amp;D)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>错误</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>因为文件系统不支持,有些文件无法移至回收站。
+是否直接删除这些文件?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>确认</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>您确定要删除所选的文件吗?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>您确定要将所选的文件移至回收站吗?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>移动文件</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>正在将下列文件移动至目标文件夹:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>复制文件</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>正在将下列文件复制至目标文件夹:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>将文件移至回收站</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>正在将下列文件移动至回收站:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>删除文件</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>正在删除下列文件:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>创建符号链接</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>正在为下列文件创建符号链接:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>更改属性</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>正在更改下列文件的属性:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>恢复已删除的文件</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>正在从回收站恢复下列文件:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>错误</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>查看文件夹内容</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>查看并修改文件夹内容</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>只读</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>读写</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>禁止</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>不同类型的文件</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>多个文件</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>选择图标</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>图像(*.png *.xpm *.svg *.svgz)</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>应用更改</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>您是否要将这些更改应用于所有子文件夹和文件?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>错误</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>您至少需要新增一个要搜索的目录。</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>选择一个文件夹</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>无法在非原生文件系统中创建链接</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>新建(&amp;N)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>粘贴(&amp;P)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>全选(&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>反选</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>排序</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>显示隐藏文件</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>文件夹属性(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>输出</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>按名称</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>按修改时间</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>按大小</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>按文件类型</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>按所有者</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>升序</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>降序</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>文件夹优先</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>区分大小写</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>名称</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>类型</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>大小</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>修改</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>所有者</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>组</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>粗体</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>斜体</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>连接(&amp;C)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>编辑路径(&amp;E)</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>复制路径(&amp;C)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>位置</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>桌面</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>计算机</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>应用程序</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>网络</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>设备</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>书签</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>回收站</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>在新标签页中打开</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>在新窗口中打开</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>清空回收站</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>隐藏</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>上移书签</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>下移书签</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>重命名书签</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>移除书签</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>卸载</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>挂载</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>弹出</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>显示所有快捷方式</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>类型: %1
+大小: %2
+修改时间: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>类型: %1
+修改时间: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>覆盖(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>重命名(&amp;R)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>位置</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>目录树</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>无法恢复文件“%s”:原路径未知</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>挂载</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>匿名连接(&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>作为用户连接(&amp;S):</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>用户名(&amp;U):</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>密码(&amp;P):</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>域(&amp;D):</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>立即遗忘密码(&amp;I)</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>记住密码直到注销(&amp;L)</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>永远记住密码(&amp;F)</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>重命名文件</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>请输入新名称:</translation>
+    </message>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>错误</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>新建文件夹</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>新建文件</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>请输入新文件名:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>新建文本文件</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>请输入新文件夹名:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>新建文件夹</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>请为新的%1输入名称:</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>自定义图标错误</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>该目录未挂载。</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>无效桌面快捷方式:“%1”</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>没有用于运行“%1”的默认应用</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>无法将工作目录切换至“%1”:%2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>确认替换文件</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;此处已有同名文件。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;是否覆盖已有文件?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation>目标</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>是否包含下列文件?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation>源文件信息</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation>目标文件信息</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation>源</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>文件名(&amp;F):</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>为所有已有文件应用此选项</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>搜索文件</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>名称/位置</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>文件名模式:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>区分大小写</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>使用正则表达式</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>搜索位置:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>新增(&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>移除(&amp;R)</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>在子目录中搜索</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>搜索隐藏文件</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>文件类型</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>仅搜索下列类型的文件:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>文本文件</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>图像文件</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>音频文件</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>视频文件</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>文档</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>文件夹</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>内容</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>文件包括:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>区分大小写(&amp;V)</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>使用正则表达式(&amp;U)</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>属性</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>文件大小:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>大于:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>字节</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>小于:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>修改时间:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>早于:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>晚于:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/translations/libfm-qt_zh_TW.ts b/src/translations/libfm-qt_zh_TW.ts
new file mode 100644 (file)
index 0000000..9860a06
--- /dev/null
@@ -0,0 +1,1560 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.1" language="zh_TW">
+<context>
+    <name>AppChooserDialog</name>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="14"/>
+        <source>Choose an Application</source>
+        <translation>選擇一個應用程式</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="36"/>
+        <source>Installed Applications</source>
+        <translation>安裝的應用程式</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="46"/>
+        <source>Custom Command</source>
+        <translation>自訂指令</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="52"/>
+        <source>Command line to execute:</source>
+        <translation>要執行的命令列:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="62"/>
+        <source>Application name:</source>
+        <translation>應用程式名稱:</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="72"/>
+        <source>&lt;b&gt;These special codes can be used in the command line:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;: Represents a single file name&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;: Represents multiple file names&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;: Represents a single URI of the file&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;: Represents multiple URIs&lt;/li&gt;
+&lt;/ul&gt;</source>
+        <translation>&lt;b&gt;這些特殊代號可以在指令列參數使用:&lt;/b&gt;
+&lt;ul&gt;
+&lt;li&gt;&lt;b&gt;%f&lt;/b&gt;:代表單一檔案名稱&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%F&lt;/b&gt;:代表多個檔案名稱&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%u&lt;/b&gt;:代表單一檔案 URI&lt;/li&gt;
+&lt;li&gt;&lt;b&gt;%U&lt;/b&gt;:代表多個 URI&lt;/li&gt;
+&lt;/ul&gt;</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="91"/>
+        <source>Keep terminal window open after command execution</source>
+        <translation>執行指令後保持終端機視窗開啟</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="98"/>
+        <source>Execute in terminal emulator</source>
+        <translation>在終端機模擬器執行</translation>
+    </message>
+    <message>
+        <location filename="../app-chooser-dialog.ui" line="109"/>
+        <source>Set selected application as default action of this file type</source>
+        <translation>將所選應用程式設定為此類型檔案的預設處理程式</translation>
+    </message>
+</context>
+<context>
+    <name>EditBookmarksDialog</name>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="14"/>
+        <source>Edit Bookmarks</source>
+        <translation>編輯書籤</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="42"/>
+        <source>Name</source>
+        <translation>名稱</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="47"/>
+        <source>Location</source>
+        <translation>位置</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="67"/>
+        <source>&amp;Add Item</source>
+        <translation>新增項目(&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="77"/>
+        <source>&amp;Remove Item</source>
+        <translation>移除項目(&amp;R)</translation>
+    </message>
+    <message>
+        <location filename="../edit-bookmarks.ui" line="102"/>
+        <source>Use drag and drop to reorder the items</source>
+        <translation>使用拖放重新排序項目</translation>
+    </message>
+</context>
+<context>
+    <name>ExecFileDialog</name>
+    <message>
+        <location filename="../exec-file.ui" line="14"/>
+        <source>Execute file</source>
+        <translation>執行檔案</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="39"/>
+        <source>&amp;Open</source>
+        <translation>開啟(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="52"/>
+        <source>E&amp;xecute</source>
+        <translation>執行(&amp;X)</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="62"/>
+        <source>Execute in &amp;Terminal</source>
+        <translation>在終端機內執行(&amp;T)</translation>
+    </message>
+    <message>
+        <location filename="../exec-file.ui" line="85"/>
+        <source>Cancel</source>
+        <translation>取消</translation>
+    </message>
+</context>
+<context>
+    <name>FileDialog</name>
+    <message>
+        <location filename="../filedialog.ui" line="22"/>
+        <source>Location:</source>
+        <translation>位置:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="65"/>
+        <source>File name:</source>
+        <translation>檔案名稱:</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.ui" line="75"/>
+        <source>File type:</source>
+        <translation>檔案類型:</translation>
+    </message>
+</context>
+<context>
+    <name>FileOperationDialog</name>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="32"/>
+        <source>Destination:</source>
+        <translation>目的地:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="55"/>
+        <source>Processing:</source>
+        <translation>正在處理:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="68"/>
+        <source>Preparing...</source>
+        <translation>準備中...</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="75"/>
+        <source>Progress</source>
+        <translation>進度</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="95"/>
+        <source>Time remaining:</source>
+        <translation>剩餘時間:</translation>
+    </message>
+    <message>
+        <location filename="../file-operation-dialog.ui" line="125"/>
+        <source>Files processed:</source>
+        <translation>已處理的檔案:</translation>
+    </message>
+</context>
+<context>
+    <name>FilePropsDialog</name>
+    <message>
+        <location filename="../file-props.ui" line="14"/>
+        <source>File Properties</source>
+        <translation>檔案屬性</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="41"/>
+        <source>General</source>
+        <translation>一般</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="80"/>
+        <source>Location:</source>
+        <translation>位置:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="106"/>
+        <source>File type:</source>
+        <translation>檔案類型:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="129"/>
+        <source>MIME type:</source>
+        <translation>Mime 類型:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="152"/>
+        <source>File size:</source>
+        <translation>檔案大小:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="175"/>
+        <source>On-disk size:</source>
+        <translation>磁碟上大小:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="198"/>
+        <source>Last modified:</source>
+        <translation>最後修改:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="221"/>
+        <source>Link target:</source>
+        <translation>連結目標:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="247"/>
+        <source>Open With:</source>
+        <translation>開啟:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="264"/>
+        <source>Last accessed:</source>
+        <translation>最後存取:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="281"/>
+        <source>Contains:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="317"/>
+        <source>Device Usage:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="349"/>
+        <source>Permissions</source>
+        <translation>權限</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="364"/>
+        <source>Ownership</source>
+        <translation>所有權</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="388"/>
+        <location filename="../file-props.ui" line="453"/>
+        <location filename="../file-props.ui" line="567"/>
+        <source>Group:</source>
+        <translation>群組:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="401"/>
+        <location filename="../file-props.ui" line="436"/>
+        <location filename="../file-props.ui" line="515"/>
+        <source>Owner:</source>
+        <translation>擁有者:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="417"/>
+        <source>Access Control</source>
+        <translation>存取控制</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="470"/>
+        <location filename="../file-props.ui" line="619"/>
+        <source>Other:</source>
+        <translation>其他:</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="487"/>
+        <source>Make the file executable</source>
+        <translation>使檔案可執行</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="528"/>
+        <location filename="../file-props.ui" line="580"/>
+        <location filename="../file-props.ui" line="632"/>
+        <source>Read</source>
+        <translation>讀取</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="541"/>
+        <location filename="../file-props.ui" line="593"/>
+        <location filename="../file-props.ui" line="645"/>
+        <source>Write</source>
+        <translation>寫入</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="554"/>
+        <location filename="../file-props.ui" line="606"/>
+        <location filename="../file-props.ui" line="658"/>
+        <source>Execute</source>
+        <translation>執行</translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="667"/>
+        <source>Sticky</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="674"/>
+        <source>SetUID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="681"/>
+        <source>SetGID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../file-props.ui" line="717"/>
+        <source>Advanced Mode</source>
+        <translation>進階模式</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserComboBox</name>
+    <message>
+        <location filename="../appchoosercombobox.cpp" line="66"/>
+        <source>Customize</source>
+        <translation>自訂</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::AppChooserDialog</name>
+    <message>
+        <location filename="../appchooserdialog.cpp" line="263"/>
+        <source>Select an application to open &quot;%1&quot; files</source>
+        <translation>選取用來開啟「%1」檔案的應用程式</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::CreateNewMenu</name>
+    <message>
+        <location filename="../createnewmenu.cpp" line="61"/>
+        <source>Folder</source>
+        <translation>資料夾</translation>
+    </message>
+    <message>
+        <location filename="../createnewmenu.cpp" line="65"/>
+        <source>Blank File</source>
+        <translation>空白檔案</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirListJob</name>
+    <message>
+        <location filename="../core/dirlistjob.cpp" line="46"/>
+        <source>The specified directory &apos;%1&apos; is not valid</source>
+        <translation>指定的目錄 &apos;%1&apos; 無效</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeModel</name>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="84"/>
+        <source>Loading...</source>
+        <translation>載入中...</translation>
+    </message>
+    <message>
+        <location filename="../dirtreemodelitem.cpp" line="267"/>
+        <location filename="../dirtreemodelitem.cpp" line="311"/>
+        <location filename="../dirtreemodelitem.cpp" line="409"/>
+        <source>&lt;No sub folders&gt;</source>
+        <translation>&lt;沒有子資料夾&gt;</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DirTreeView</name>
+    <message>
+        <location filename="../dirtreeview.cpp" line="211"/>
+        <source>Open in New T&amp;ab</source>
+        <translation>在新分頁開啟 (&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="215"/>
+        <source>Open in New Win&amp;dow</source>
+        <translation>在新視窗開啟 (&amp;D)</translation>
+    </message>
+    <message>
+        <location filename="../dirtreeview.cpp" line="220"/>
+        <source>Open in Termina&amp;l</source>
+        <translation>在終端機內開啟 (&amp;I)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::DndActionMenu</name>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="32"/>
+        <source>Copy here</source>
+        <translation>複製到這裡</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="35"/>
+        <source>Move here</source>
+        <translation>移動到這裡</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="38"/>
+        <source>Create symlink here</source>
+        <translation>建立符號連結到這裡</translation>
+    </message>
+    <message>
+        <location filename="../dndactionmenu.cpp" line="41"/>
+        <source>Cancel</source>
+        <translation>取消</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::EditBookmarksDialog</name>
+    <message>
+        <location filename="../editbookmarksdialog.cpp" line="92"/>
+        <source>New bookmark</source>
+        <translation>新書籤</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::ExecFileDialog</name>
+    <message>
+        <location filename="../execfiledialog.cpp" line="40"/>
+        <source>This file &apos;%1&apos; seems to be a desktop entry.
+What do you want to do with it?</source>
+        <translation>檔案 &apos;%1&apos; 看起來是桌面捷徑檔。
+你想要對它進行什麼操作?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="46"/>
+        <source>This text file &apos;%1&apos; seems to be an executable script.
+What do you want to do with it?</source>
+        <translation>這個文字檔 &apos;%1&apos; 似乎是可執行的 script。
+想要進行什麼操作?</translation>
+    </message>
+    <message>
+        <location filename="../execfiledialog.cpp" line="51"/>
+        <source>This file &apos;%1&apos; is executable. Do you want to execute it?</source>
+        <translation>這個檔案 &apos;%1&apos; 是可執行檔,是否想要執行?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialog</name>
+    <message>
+        <location filename="../filedialog.cpp" line="102"/>
+        <source>Go Back</source>
+        <translation>上一頁</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="103"/>
+        <source>Alt+Left</source>
+        <comment>Go Back</comment>
+        <translation>Alt+Left</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="109"/>
+        <source>Go Forward</source>
+        <translation>下一頁</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="110"/>
+        <source>Alt+Right</source>
+        <comment>Go Forward</comment>
+        <translation>Alt+Right</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="117"/>
+        <source>Reload</source>
+        <translation>重新整理</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="118"/>
+        <source>F5</source>
+        <comment>Reload</comment>
+        <translation>F5</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="135"/>
+        <source>Create Folder</source>
+        <translation>建立資料夾</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="142"/>
+        <source>Icon View</source>
+        <translation>圖示檢視</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="146"/>
+        <source>Thumbnail View</source>
+        <translation>縮圖檢視</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="150"/>
+        <source>Compact View</source>
+        <translation>精簡檢視</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="154"/>
+        <source>Detailed List View</source>
+        <translation>詳細清單檢視</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <location filename="../filedialog.cpp" line="634"/>
+        <source>Error</source>
+        <translation>錯誤</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="271"/>
+        <source>Please select a file</source>
+        <translation>請選擇一個檔案</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="295"/>
+        <source>%1 already exists.
+Do you want to replace it?</source>
+        <translation>%1 已經存在。
+你想要取代它嗎?</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="604"/>
+        <source>Path &quot;%1&quot; does not exist</source>
+        <translation>路徑 &quot;%1&quot; 不存在</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="618"/>
+        <source>&quot;%1&quot; is not a directory</source>
+        <translation>&quot;%1&quot; 不是一個目錄</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="624"/>
+        <source>&quot;%1&quot; is not a file</source>
+        <translation>&quot;%1&quot; 不是一個檔案</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="751"/>
+        <location filename="../filedialog.cpp" line="859"/>
+        <source>&amp;Open</source>
+        <translation>開啟(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="754"/>
+        <location filename="../filedialog.cpp" line="867"/>
+        <source>&amp;Save</source>
+        <translation>儲存 (&amp;S)</translation>
+    </message>
+    <message>
+        <location filename="../filedialog.cpp" line="761"/>
+        <source>All Files (*)</source>
+        <translation>所有檔案 (*)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileDialogHelper</name>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="133"/>
+        <source>Open File</source>
+        <translation>開啟檔案</translation>
+    </message>
+    <message>
+        <location filename="../filedialoghelper.cpp" line="134"/>
+        <source>Save File</source>
+        <translation>儲存檔案</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileMenu</name>
+    <message>
+        <location filename="../filemenu.cpp" line="84"/>
+        <source>Open</source>
+        <translation>開啟</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="88"/>
+        <source>Open With...</source>
+        <translation>用其他程式開啟...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="116"/>
+        <source>Other Applications</source>
+        <translation>其他應用程式</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="122"/>
+        <source>Create &amp;New</source>
+        <translation>新建(&amp;N)</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="141"/>
+        <source>&amp;Restore</source>
+        <translation>恢復(&amp;R)</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="147"/>
+        <source>Cut</source>
+        <translation>剪下</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="151"/>
+        <source>Copy</source>
+        <translation>複製</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="155"/>
+        <source>Paste</source>
+        <translation>貼上</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="159"/>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Move to Trash</source>
+        <translation>移動到垃圾桶(&amp;M)</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="252"/>
+        <source>Trust selected executables</source>
+        <translation>信任已選擇的執行檔</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="253"/>
+        <source>Trust this executable</source>
+        <translation>信任此執行檔</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="341"/>
+        <source>Output</source>
+        <translation>輸出</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="406"/>
+        <source>&amp;Delete</source>
+        <translation>刪除(&amp;D)</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="163"/>
+        <source>Rename</source>
+        <translation>重新命名</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="217"/>
+        <source>Extract to...</source>
+        <translation>解壓縮到...</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="225"/>
+        <source>Extract Here</source>
+        <translation>在此解壓縮</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="232"/>
+        <source>Compress</source>
+        <translation>壓縮</translation>
+    </message>
+    <message>
+        <location filename="../filemenu.cpp" line="241"/>
+        <source>Properties</source>
+        <translation>屬性</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperation</name>
+    <message>
+        <location filename="../fileoperation.cpp" line="309"/>
+        <source>Error</source>
+        <translation>錯誤</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="310"/>
+        <source>Some files cannot be moved to trash can because the underlying file systems don&apos;t support this operation.
+Do you want to delete them instead?</source>
+        <translation>因為檔案系統不支援,有些檔案無法丟到垃圾桶
+是否直接刪除這些檔案?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="375"/>
+        <location filename="../fileoperation.cpp" line="392"/>
+        <source>Confirm</source>
+        <translation>確認</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="376"/>
+        <source>Do you want to delete the selected files?</source>
+        <translation>你確定要刪除選取的檔案嗎?</translation>
+    </message>
+    <message>
+        <location filename="../fileoperation.cpp" line="393"/>
+        <source>Do you want to move the selected files to trash can?</source>
+        <translation>你確定要把選取的檔案移到垃圾桶嗎?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileOperationDialog</name>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="46"/>
+        <source>Move files</source>
+        <translation>移動檔案</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="47"/>
+        <source>Moving the following files to destination folder:</source>
+        <translation>正在移動下列檔案到目的資料夾:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="50"/>
+        <source>Copy Files</source>
+        <translation>複製檔案</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="51"/>
+        <source>Copying the following files to destination folder:</source>
+        <translation>正在複製下列檔案到目的資料夾:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="54"/>
+        <source>Trash Files</source>
+        <translation>將檔案丟到垃圾桶</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="55"/>
+        <source>Moving the following files to trash can:</source>
+        <translation>正在將下列檔案移動到垃圾桶:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="58"/>
+        <source>Delete Files</source>
+        <translation>刪除檔案</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="59"/>
+        <source>Deleting the following files:</source>
+        <translation>正在刪除下列檔案:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="64"/>
+        <source>Create Symlinks</source>
+        <translation>建立符號連結</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="65"/>
+        <source>Creating symlinks for the following files:</source>
+        <translation>正在建立下列檔案的符號連結:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="68"/>
+        <source>Change Attributes</source>
+        <translation>改變屬性</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="69"/>
+        <source>Changing attributes of the following files:</source>
+        <translation>正在更改下列檔案的屬性:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="74"/>
+        <source>Restore Trashed Files</source>
+        <translation>恢復被刪除的檔案</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="75"/>
+        <source>Restoring the following files from trash can:</source>
+        <translation>正在從垃圾桶恢復下列被刪除的檔案:</translation>
+    </message>
+    <message>
+        <location filename="../fileoperationdialog.cpp" line="146"/>
+        <location filename="../fileoperationdialog.cpp" line="152"/>
+        <source>Error</source>
+        <translation>錯誤</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FilePropsDialog</name>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="162"/>
+        <source>View folder content</source>
+        <translation>檢視資料夾內容</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="163"/>
+        <source>View and modify folder content</source>
+        <translation>檢視及修改資料夾內容</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="167"/>
+        <source>Read</source>
+        <translation>讀取</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="168"/>
+        <source>Read and write</source>
+        <translation>讀取及寫入</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="170"/>
+        <source>Forbidden</source>
+        <translation>禁止</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="287"/>
+        <source>Files of different types</source>
+        <translation>不同類型的檔案</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="310"/>
+        <source>Multiple Files</source>
+        <translation>多個檔案</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="333"/>
+        <source>%p% used</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="334"/>
+        <source>%1 Free of %2</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="378"/>
+        <source>no file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="380"/>
+        <source>one file</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="382"/>
+        <source>%1 files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="416"/>
+        <source>Select an icon</source>
+        <translation>選擇一個圖示</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="418"/>
+        <source>Images (*.png *.xpm *.svg *.svgz )</source>
+        <translation>影像檔 (*.png *.xpm *.svg *.svgz )</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="514"/>
+        <source>Apply changes</source>
+        <translation>套用變更</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="515"/>
+        <source>Do you want to recursively apply these changes to all files and sub-folders?</source>
+        <translation>你是否想將這些變更套用到所有子資料夾和其內的檔案?</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileSearchDialog</name>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>Error</source>
+        <translation>錯誤</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="128"/>
+        <source>You should add at least one directory to search.</source>
+        <translation>你至少需要新增一個要搜尋的目錄。</translation>
+    </message>
+    <message>
+        <location filename="../filesearchdialog.cpp" line="135"/>
+        <source>Select a folder</source>
+        <translation>選擇一個資料夾</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FileTransferJob</name>
+    <message>
+        <location filename="../core/filetransferjob.cpp" line="497"/>
+        <source>Cannot create a link on non-native filesystem</source>
+        <translation>無法在非原生檔案系統上建立連結</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderMenu</name>
+    <message>
+        <location filename="../foldermenu.cpp" line="41"/>
+        <source>Create &amp;New</source>
+        <translation>新建(&amp;N)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="48"/>
+        <source>&amp;Paste</source>
+        <translation>貼上(&amp;P)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="54"/>
+        <source>Select &amp;All</source>
+        <translation>全選(&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="58"/>
+        <source>Invert Selection</source>
+        <translation>反向選取</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="64"/>
+        <source>Sorting</source>
+        <translation>排序</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="69"/>
+        <source>Show Hidden</source>
+        <translation>顯示隱藏檔</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="97"/>
+        <source>Folder Pr&amp;operties</source>
+        <translation>資料夾屬性(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="140"/>
+        <source>Output</source>
+        <translation>輸出</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="163"/>
+        <source>By File Name</source>
+        <translation>依照檔名</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="164"/>
+        <source>By Modification Time</source>
+        <translation>依照修改時間</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="165"/>
+        <source>By File Size</source>
+        <translation>依照檔案大小</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="166"/>
+        <source>By File Type</source>
+        <translation>依照檔案型態</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="167"/>
+        <source>By File Owner</source>
+        <translation>依照檔案所有者</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="179"/>
+        <source>Ascending</source>
+        <translation>升冪排列</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="184"/>
+        <source>Descending</source>
+        <translation>降冪排列</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="201"/>
+        <source>Folder First</source>
+        <translation>資料夾優先</translation>
+    </message>
+    <message>
+        <location filename="../foldermenu.cpp" line="211"/>
+        <source>Case Sensitive</source>
+        <translation>區分大小寫</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FolderModel</name>
+    <message>
+        <location filename="../foldermodel.cpp" line="275"/>
+        <source>Name</source>
+        <translation>名稱</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="278"/>
+        <source>Type</source>
+        <translation>類型</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="281"/>
+        <source>Size</source>
+        <translation>大小</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="284"/>
+        <source>Modified</source>
+        <translation>修改</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="287"/>
+        <source>Owner</source>
+        <translation>所有者</translation>
+    </message>
+    <message>
+        <location filename="../foldermodel.cpp" line="290"/>
+        <source>Group</source>
+        <translation>群組</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::FontButton</name>
+    <message>
+        <location filename="../fontbutton.cpp" line="46"/>
+        <source>Bold</source>
+        <translation>粗體</translation>
+    </message>
+    <message>
+        <location filename="../fontbutton.cpp" line="50"/>
+        <source>Italic</source>
+        <translation>斜體</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mountoperationpassworddialog.cpp" line="40"/>
+        <source>&amp;Connect</source>
+        <translation>連接(&amp;C)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PathBar</name>
+    <message>
+        <location filename="../pathbar.cpp" line="128"/>
+        <source>&amp;Edit Path</source>
+        <translation>編輯路徑 (&amp;E)</translation>
+    </message>
+    <message>
+        <location filename="../pathbar.cpp" line="131"/>
+        <source>&amp;Copy Path</source>
+        <translation>複製路徑 (&amp;C)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesModel</name>
+    <message>
+        <location filename="../placesmodel.cpp" line="45"/>
+        <source>Places</source>
+        <translation>位置</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="53"/>
+        <source>Desktop</source>
+        <translation>桌面</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="225"/>
+        <source>Trash</source>
+        <translation>垃圾桶</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="59"/>
+        <source>Computer</source>
+        <translation>電腦</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="67"/>
+        <source>Applications</source>
+        <translation>應用程式</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="76"/>
+        <source>Network</source>
+        <translation>網路</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="80"/>
+        <source>Devices</source>
+        <translation>裝置</translation>
+    </message>
+    <message>
+        <location filename="../placesmodel.cpp" line="134"/>
+        <source>Bookmarks</source>
+        <translation>書籤</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::PlacesView</name>
+    <message>
+        <location filename="../placesview.cpp" line="500"/>
+        <source>Empty Trash</source>
+        <translation>清空垃圾桶</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="485"/>
+        <source>Open in New Tab</source>
+        <translation>在新分頁中開啟</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="488"/>
+        <source>Open in New Window</source>
+        <translation>在新視窗中開啟</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="520"/>
+        <location filename="../placesview.cpp" line="577"/>
+        <source>Hide</source>
+        <translation>隱藏</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="537"/>
+        <source>Move Bookmark Up</source>
+        <translation>往上移動書籤</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="542"/>
+        <source>Move Bookmark Down</source>
+        <translation>往下移動書籤</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="546"/>
+        <source>Rename Bookmark</source>
+        <translation>重新命名書籤</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="549"/>
+        <source>Remove Bookmark</source>
+        <translation>移除書籤</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="558"/>
+        <location filename="../placesview.cpp" line="591"/>
+        <source>Unmount</source>
+        <translation>卸載</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="562"/>
+        <source>Mount</source>
+        <translation>掛載</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="568"/>
+        <source>Eject</source>
+        <translation>退出</translation>
+    </message>
+    <message>
+        <location filename="../placesview.cpp" line="603"/>
+        <source>Show All Entries</source>
+        <translation>顯示全部的項目</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::RenameDialog</name>
+    <message>
+        <location filename="../renamedialog.cpp" line="58"/>
+        <location filename="../renamedialog.cpp" line="78"/>
+        <source>Type: %1
+Size: %2
+Modified: %3</source>
+        <translation>類型: %1
+大小: %2
+最後修改: %3</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="64"/>
+        <location filename="../renamedialog.cpp" line="84"/>
+        <source>Type: %1
+Modified: %2</source>
+        <translation>類型: %1
+最後修改: %2</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="97"/>
+        <source>&amp;Overwrite</source>
+        <translation>覆蓋(&amp;O)</translation>
+    </message>
+    <message>
+        <location filename="../renamedialog.cpp" line="99"/>
+        <source>&amp;Rename</source>
+        <translation>重新命名(&amp;R)</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::SidePane</name>
+    <message>
+        <location filename="../sidepane.cpp" line="45"/>
+        <source>Places</source>
+        <translation>位置</translation>
+    </message>
+    <message>
+        <location filename="../sidepane.cpp" line="46"/>
+        <source>Directory Tree</source>
+        <translation>目錄樹</translation>
+    </message>
+</context>
+<context>
+    <name>Fm::UntrashJob</name>
+    <message>
+        <location filename="../core/untrashjob.cpp" line="35"/>
+        <source>Cannot untrash file &apos;%s&apos;: original path not known</source>
+        <translation>無法將檔案從垃圾桶復原 &apos;%s&apos;: 原來路徑未知</translation>
+    </message>
+</context>
+<context>
+    <name>MountOperationPasswordDialog</name>
+    <message>
+        <location filename="../mount-operation-password.ui" line="20"/>
+        <source>Mount</source>
+        <translation>掛載</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="48"/>
+        <source>Connect &amp;anonymously</source>
+        <translation>匿名連線(&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="58"/>
+        <source>Connect as u&amp;ser:</source>
+        <translation>以使用者帳號連線(&amp;S):</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="79"/>
+        <source>&amp;Username:</source>
+        <translation>使用者名稱(&amp;U):</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="102"/>
+        <source>&amp;Password:</source>
+        <translation>密碼(&amp;P):</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="112"/>
+        <source>&amp;Domain:</source>
+        <translation>域名(&amp;D):</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="127"/>
+        <source>Forget password &amp;immediately</source>
+        <translation>立刻忘記密碼(&amp;I)</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="137"/>
+        <source>Remember password until you &amp;logout</source>
+        <translation>記住密碼直到登出(&amp;L)</translation>
+    </message>
+    <message>
+        <location filename="../mount-operation-password.ui" line="147"/>
+        <source>Remember &amp;forever</source>
+        <translation>永遠記住密碼(&amp;F)</translation>
+    </message>
+</context>
+<context>
+    <name>QObject</name>
+    <message>
+        <location filename="../filelauncher.cpp" line="101"/>
+        <location filename="../filepropsdialog.cpp" line="539"/>
+        <location filename="../mountoperation.cpp" line="207"/>
+        <location filename="../utilities.cpp" line="169"/>
+        <location filename="../utilities.cpp" line="263"/>
+        <source>Error</source>
+        <translation>錯誤</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="178"/>
+        <source>Rename File</source>
+        <translation>重新命名</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="179"/>
+        <source>Please enter a new name:</source>
+        <translation>請輸入一個新名稱:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="204"/>
+        <source>Create Folder</source>
+        <translation>建立資料夾</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="209"/>
+        <source>Please enter a new file name:</source>
+        <translation>請輸入一個新檔名:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="214"/>
+        <source>Please enter a new folder name:</source>
+        <translation>請輸入一個新資料夾名稱:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="215"/>
+        <source>New folder</source>
+        <translation>新資料夾</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="210"/>
+        <source>New text file</source>
+        <translation>新文字檔</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="220"/>
+        <source>Enter a name for the new %1:</source>
+        <translation>幫新的 %1 輸入一個名稱:</translation>
+    </message>
+    <message>
+        <location filename="../utilities.cpp" line="205"/>
+        <source>Create File</source>
+        <translation>建立檔案</translation>
+    </message>
+    <message>
+        <location filename="../filepropsdialog.cpp" line="556"/>
+        <source>Custom Icon Error</source>
+        <translation>自訂圖示錯誤</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="41"/>
+        <source>The path is not mounted.</source>
+        <translation>這個路徑尚未掛載。</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="272"/>
+        <source>Invalid desktop entry file: &apos;%1&apos;</source>
+        <translation>無效的桌面捷徑檔: &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="307"/>
+        <source>No default application is set to launch &apos;%1&apos;</source>
+        <translation>沒有設定預設的應用程式來啟動 &apos;%1&apos;</translation>
+    </message>
+    <message>
+        <location filename="../core/basicfilelauncher.cpp" line="348"/>
+        <source>Cannot set working directory to &apos;%1&apos;: %2</source>
+        <translation>無法將目前工作目錄設定為 &apos;%1&apos;: %2</translation>
+    </message>
+    <message>
+        <location filename="../placesmodelitem.cpp" line="123"/>
+        <source>Identifier: </source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>RenameDialog</name>
+    <message>
+        <location filename="../rename-dialog.ui" line="14"/>
+        <source>Confirm to replace files</source>
+        <translation>確認取代檔案</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="35"/>
+        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;There is already a file with the same name in this location.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Do you want to replace the existing file?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;這個位置已經有一個同名的檔案&lt;/span&gt;&lt;/p&gt;&lt;p&gt;你要取代現有的檔案嗎?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="56"/>
+        <source>dest</source>
+        <translation type="unfinished">目標</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="63"/>
+        <source>with the following file?</source>
+        <translation>用下列檔案?</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="76"/>
+        <source>src file info</source>
+        <translation type="unfinished">來源檔案資訊</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="89"/>
+        <source>dest file info</source>
+        <translation type="unfinished">目標檔案資訊</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="102"/>
+        <source>src</source>
+        <translation type="unfinished">來源</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="122"/>
+        <source>&amp;File name:</source>
+        <translation>檔名(&amp;F):</translation>
+    </message>
+    <message>
+        <location filename="../rename-dialog.ui" line="137"/>
+        <source>Apply this option to all existing files</source>
+        <translation>套用這個選項到所有已存在的檔案</translation>
+    </message>
+</context>
+<context>
+    <name>SearchDialog</name>
+    <message>
+        <location filename="../filesearch.ui" line="14"/>
+        <source>Search Files</source>
+        <translation>搜尋檔案</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="29"/>
+        <source>Name/Location</source>
+        <translation>名稱/位置</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="35"/>
+        <source>File Name Patterns:</source>
+        <translation>檔案名稱樣式:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="41"/>
+        <source>*</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="48"/>
+        <source>Case insensitive</source>
+        <translation>區分大小寫</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="55"/>
+        <source>Use regular expression</source>
+        <translation>使用正規表示式 (regexp)</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="68"/>
+        <source>Places to Search:</source>
+        <translation>要搜尋的地方:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="81"/>
+        <source>&amp;Add</source>
+        <translation>新增 (&amp;A)</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="93"/>
+        <source>&amp;Remove</source>
+        <translation>移除 (&amp;R)</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="122"/>
+        <source>Search in sub directories</source>
+        <translation>在子目錄中搜尋</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="129"/>
+        <source>Search for hidden files</source>
+        <translation>搜尋隱藏檔</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="140"/>
+        <source>File Type</source>
+        <translation>檔案型態</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="146"/>
+        <source>Only search for files of following types:</source>
+        <translation>只搜尋下列型態的檔案:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="152"/>
+        <source>Text files</source>
+        <translation>文字檔</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="159"/>
+        <source>Image files</source>
+        <translation>圖片檔</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="166"/>
+        <source>Audio files</source>
+        <translation>聲音檔</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="173"/>
+        <source>Video files</source>
+        <translation>影片檔</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="180"/>
+        <source>Documents</source>
+        <translation>文件</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="187"/>
+        <source>Folders</source>
+        <translation>資料夾</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="211"/>
+        <source>Content</source>
+        <translation>內容</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="217"/>
+        <source>File contains:</source>
+        <translation>檔案包含:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="226"/>
+        <source>Case insensiti&amp;ve</source>
+        <translation>區分大小寫 (&amp;V)</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="233"/>
+        <source>&amp;Use regular expression</source>
+        <translation>使用正規表示式 (&amp;U)</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="260"/>
+        <source>Properties</source>
+        <translation>屬性</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="266"/>
+        <source>File Size:</source>
+        <translation>檔案大小:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="295"/>
+        <source>Larger than:</source>
+        <translation>大於:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="309"/>
+        <location filename="../filesearch.ui" line="339"/>
+        <source>Bytes</source>
+        <translation>位元組</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="314"/>
+        <location filename="../filesearch.ui" line="344"/>
+        <source>KiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="319"/>
+        <location filename="../filesearch.ui" line="349"/>
+        <source>MiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="324"/>
+        <location filename="../filesearch.ui" line="354"/>
+        <source>GiB</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="274"/>
+        <source>Smaller than:</source>
+        <translation>小於:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="370"/>
+        <source>Last Modified Time:</source>
+        <translation>最後修改時間:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="378"/>
+        <source>Earlier than:</source>
+        <translation>早於:</translation>
+    </message>
+    <message>
+        <location filename="../filesearch.ui" line="385"/>
+        <source>Later than:</source>
+        <translation>晚於:</translation>
+    </message>
+</context>
+</TS>
diff --git a/src/utilities.cpp b/src/utilities.cpp
new file mode 100644 (file)
index 0000000..8820587
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "utilities.h"
+#include "utilities_p.h"
+#include <QApplication>
+#include <QClipboard>
+#include <QMimeData>
+#include <QUrl>
+#include <QList>
+#include <QStringBuilder>
+#include <QMessageBox>
+#include "fileoperation.h"
+#include <QEventLoop>
+
+#include <pwd.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <glib.h>
+
+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<QUrl> 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<Fm::FilePathList, bool> 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<const Fm::FileInfo> 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
new file mode 100644 (file)
index 0000000..3304b56
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2013 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef FM_UTILITIES_H
+#define FM_UTILITIES_H
+
+#include "libfmqtglobals.h"
+#include <QUrl>
+#include <QList>
+#include <QMimeData>
+#include <sys/types.h>
+
+#include <cstdint>
+#include <utility>
+
+#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<QUrl> urls);
+
+LIBFM_QT_API std::pair<Fm::FilePathList, bool> 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<const Fm::FileInfo> 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
new file mode 100644 (file)
index 0000000..7852f99
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 - 2015  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __UTILITS_P_H__
+#define __UTILITS_P_H__
+
+#include <QInputDialog>
+#include <QTimer>
+#include <QLineEdit>
+
+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<QLineEdit*>();
+    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
new file mode 100644 (file)
index 0000000..558b52e
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef __LIBFM_QT_FM_UTILS_H__
+#define __LIBFM_QT_FM_UTILS_H__
+
+#include <QObject>
+#include <QtGlobal>
+#include "libfmqtglobals.h"
+
+
+namespace Fm {
+
+
+
+}
+
+#endif // __LIBFM_QT_FM_UTILS_H__
diff --git a/src/xdndworkaround.cpp b/src/xdndworkaround.cpp
new file mode 100644 (file)
index 0000000..f0a35d2
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2016  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <QtGlobal>
+#include "xdndworkaround.h"
+#include <QApplication>
+#include <QDebug>
+#include <QX11Info>
+#include <QMimeData>
+#include <QCursor>
+#include <QWidget>
+
+// This part is for Qt >= 5.4 only
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
+#include <QDrag>
+#include <QUrl>
+#include <string.h>
+
+// these are private Qt headers which are not part of Qt APIs
+#include <private/qdnd_p.h>  // 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<xcb_generic_event_t*>(message);
+        switch(event->response_type & ~0x80) {
+        case XCB_CLIENT_MESSAGE:
+            return clientMessage(reinterpret_cast<xcb_client_message_event_t*>(event));
+        case XCB_SELECTION_NOTIFY:
+            return selectionNotify(reinterpret_cast<xcb_selection_notify_event_t*>(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<xcb_selection_request_event_t*>(event));
+        case XCB_GE_GENERIC:
+            // newer versions of Qt5 supports xinput2, which sends its mouse events via XGE.
+            return genericEvent(reinterpret_cast<xcb_ge_generic_event_t*>(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<QUrl> 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*)&notify);
+            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
new file mode 100644 (file)
index 0000000..9d40c11
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016  Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/*
+ * 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 <QtGlobal>
+
+#include <QObject>
+#include <QAbstractNativeEventFilter>
+#include <xcb/xcb.h>
+#include <QByteArray>
+#include <QPointer>
+
+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<QDrag> lastDrag_;
+    // xinput related
+    bool xinput2Enabled_;
+    int xinputOpCode_;
+    int xinputEventBase_;
+    int xinputErrorBase_;
+#endif // Qt >= 5.4
+};
+
+#endif // XDNDWORKAROUND_H